Chapter 4. Work Smart, Not Hard with Functional Code

Everything I’ve covered so far has been FP as intended by Microsoft’s C# team. You’ll find these features, along with examples, on the Microsoft website. In this chapter, however, I want to start being a bit more creative with C#.

I don’t know about you, but I like being lazy, or at least I don’t like wasting my time with tedious boilerplate code. One of the many wonderful things about FP is its concision, compared to imperative code.

In this chapter, I’m going to show you ways to push the functional envelope further than out-of-the-box C# will allow. You’ll also learn to implement some of the more recent functional features introduced in C# in legacy versions of the language, which will hopefully allow you to get on with your day job an awfully lot quicker.

This chapter explores a few categories of functional concepts:

Funcs in enumerables

Func delegates don’t seem to get used all that much, but they’re incredibly powerful features of C#. I’ll show a few ways of using them that help extend C#’s capabilities. In this case, we’ll add them to enumerables and operate on them with LINQ expressions.

Funcs as filters

You can also use Func delegates as filters—something that sits between you and the real value you’re trying to reach. You can write a few neat bits of code by using these principles.

Custom enumerables

I’ve discussed the IEnumerable interface and how cool it is before, but did you know you can break it open and implement your own customized behavior? I’ll show you how.

All these and a host of other concepts too!

It’s Time to Get Func-y

The Func delegate types are functions stored as variables. You define what parameters they take and what they return, and call them like any other function. Here’s a quick example:

private readonly Func<Person, DateTime, string> SayHello =
    (Person p, DateTime today) => today + " : " + "Hello " + p.Name;

The last generic type in the list between the two angle brackets is the return value; all the previous types are the parameters. This example takes two string parameters and returns a string.

You’re going to be seeing an awful lot of Func delegates from now on, so please do make sure you’re comfortable with them before reading on.

Funcs in Enumerables

I’ve seen plenty of examples of Funcs as parameters to functions, but I’m not sure many developers realize that we can put them in an enumerable and create some interesting behaviors.

First is the obvious one—put them in an array to act on the same data multiple times:

private IEnumerable<Func<Employee, string>> descriptors = new []
{
    x => "First Name = " + x.firstName,
    x => "Last Name = " + x.lastName,
    x => "MiddleNames = string.Join(" ", x.MiddleNames)
}

public string DescribeEmployee(Employee emp) =>
   string.Join(Environment.NewLine, descriptors.Select(x => x(emp)));

Using this technique, we can have a single original source of data (here, an Employee object) and have multiple records of the same type generated from it. In this case, we aggregate using the built-in .NET method string.Join to present a single, unified string to the end user.

This approach has a few advantages over a simple StringBuilder. First, the array can be assembled dynamically. We could have multiple rules for each property and how it’s rendered, which could be selected from a set of local variables depending on custom logic.

Second, this is an enumerable, so by defining it this way, we’re taking advantage of a feature of enumerables called lazy evaluation (introduced in Chapter 2). The thing about enumerables is that they aren’t arrays; they aren’t even data. They’re just pointers to something that will tell us how to extract the data. It might well be—and, in fact, usually is the case—that the source behind the enumerable is a simple array, but not necessarily. An enumerable requires a function to be executed each time the next item is accessed via a foreach loop. Enumerables were developed to transform into actual data only at the very last possible moment—typically, when starting a foreach loop iteration. Most of the time, this doesn’t matter if an array held in-memory somewhere is feeding the enumerable, but if an expensive function or lookup to an external system is powering it, lazy loading can be incredibly useful to prevent unnecessary work.

The elements of an enumerable are evaluated one at a time and only when their turn has come to be used by whatever process is performing the enumeration. For example, if we use the LINQ Any function to evaluate each element in an enumerable, Any will stop enumerating the first time an element is found that matches the specified criteria, meaning the remaining elements will be left unevaluated.

Lastly, from a maintenance perspective, this technique is easier to live with. Adding a new line to the final result is as easy as adding a new element to the array. This approach also acts as a restraint to future programmers, making it harder for them to try to put too much complex logic where it doesn’t belong.

A Super-Simple Validator

Let’s imagine a quick validation function, which typically looks like this:

public bool IsPasswordValid(string password)
{
    if(password.Length <= 6)
        return false;

    if(password.Length > 20)
        return false;

    if(!password.Any(x => Char.IsLower(x)))
        return false;

    if(!password.Any(x => Char.IsUpper(x)))
        return false;

    if(!password.Any(x => Char.IsSymbol(x)))
        return false;

    if(password.Contains("Justin", StringComparison.OrdinalIgnoreCase)
        && password.Contains("Bieber", StringComparison.OrdinalIgnoreCase))
            return false;

    return true;
}

Well, for a start, that’s a lot of code for what is, in fact, a fairly simple set of rules. The imperative approach forces you to write a whole heap of repetitive boilerplate code. On top of that, if we want to add in another rule, that’s potentially around four new lines of code to add when really only one is especially interesting to us.

If only there were a way to compact this code into just a few simple lines. Well, since you asked so nicely, here you go:

public bool IsPasswordValid(string password) =>
    new Func<string, bool>[]
    {
        x => x.Length > 6,
        x => x.Length <= 20,
        x => x.Any(y => Char.IsLower(y)),
        x => x.Any(y => Char.IsUpper(y)),
        x => x.Any(y => Char.IsSymbol(y)),
        x => !x.Contains("Justin", StringComparison.OrdinalIgnoreCase)
            && !x.Contains("Bieber", StringComparison.OrdinalIgnoreCase)
    }.All(f => f(password));

Not so long now, is it? What have we done here? We’ve put all the rules into an array of Funcs that turn a string into a bool—i.e., check a single validation rule. We use a LINQ statement: .All(). The purpose of this function is to evaluate whatever lambda expression we give it against all elements of the array it’s attached to. If a single one of these returns false, the process is terminated early, and false is returned from All() (as mentioned earlier, the subsequent values aren’t accessed, so lazy evaluation saves us time by not evaluating them). If every single one of the items returns true, All() also returns true.

We’ve effectively re-created the first code sample, but the boilerplate code we were forced to write—if statements and early returns—is now implicit in the structure.

This also has the advantage of once again being easy to maintain as a code structure. If we wanted, we could even generalize it into an extension method. I do this often:

public static bool IsValid<T>(this T @this, params Func<T,bool>[] rules) =>
    rules.All(x => x(@this));

This reduces the size of the password validator yet further and gives us a handy, generic structure to use elsewhere:

public bool IsPasswordValid(string password) =>
    password.IsValid(
        x => x.Length > 6,
        x => x.Length <= 20,
        x => x.Any(y => Char.IsLower(y)),
        x => x.Any(y => Char.IsUpper(y)),
        x => x.Any(y => Char.IsSymbol(y)),
        x => !x.Contains("Justin", StringComparison.OrdinalIgnoreCase)
            && !x.Contains("Bieber", StringComparison.OrdinalIgnoreCase)
    )

At this point, I hope you’re reconsidering ever writing something as long and ungainly as that first validation code sample ever again.

I think an IsValid check is easier to read and maintain, but if we want a piece of code that is much more in line with the original code sample, we can create a new extension method by using Any() instead of All():

public static bool IsInvalid<T>(
    this T @this,
    params Func<string,bool>[] rules) =>

This means that the Boolean logic of each array element can be reversed, as it was originally:

public bool IsPasswordValid(string password) =>
    !password.IsInvalid(
        x => x.Length <= 6,
        x => x.Length > 20,
        x => !x.Any(y => Char.IsLower(y)),
        x => !x.Any(y => Char.IsUpper(y)),
        x => !x.Any(y => Char.IsSymbol(y)),
        x => x.Contains("Justin", StringComparison.OrdinalIgnoreCase)
            && x.Contains("Bieber", StringComparison.OrdinalIgnoreCase)
    )

If we want to maintain both functions, IsValid() and IsInvalid(), because each has its place in our codebase, it’s probably worth saving some coding effort and preventing a potential maintenance task in the future by simply referencing one in the other:

public static bool IsValid<T>(this T @this, params Func<T,bool>[] rules) =>
    rules.All(x => x(@this));

public static bool IsInvalid<T>(this T @this, params Func<T,bool>[] rules) =>
    !@this.IsValid(rules);

Use it wisely, my young functional Padawan learner.

Pattern Matching for Old Versions of C#

Pattern matching is one of the best features of C# in recent years, along with record types, but it isn’t available in anything except the most recent .NET versions. (See Chapter 3 for more details on native pattern matching in C# 7 and up.)

Is there a way to allow pattern matching to happen, but without needing up upgrade to a newer version of C#? There most certainly is. It is nowhere near as elegant as the native syntax in C# 8, but it provides a few of the same benefits.

In this example, we’ll calculate the amount of tax someone should pay based on a grossly simplified version of the UK income tax rules. Note that these really are much simpler than the real thing. I don’t want us to get too bogged down in the complexities of taxes.

The rules to apply look like this:

  • If yearly income is less than or equal to £12,570, no tax is taken.

  • If yearly income is between £12,571 and £50,270, take 20% tax.

  • If yearly income is between £50,271 and £150,000, take 40% tax.

  • If yearly income is over £150,000, take 45% tax.

If we wanted to write this longhand (nonfunctionally), it would look like this:

decimal ApplyTax(decimal income)
{
    if (income <= 12570)
        return income;
    else if (income <=50270)
        return income * 0.8M;
    else if (income <= 150000)
        return income * 0.6M;
    else
        return income * 0.55M;
}

Now, in C# 8 and onward, switch expressions would compress this to a few lines. So long as we’re running at least C# 7 (.NET Framework 4.7), this is the style of pattern matching we can create:

var inputValue = 25000M;
var updatedValue = inputValue.Match(
    (x => x <= 12570, x => x),
    (x => x <= 50270, x => x * 0.8M),
    (x => x <= 150000, x => x * 0.6M)
).DefaultMatch(x => x * 0.55M);

We’re passing in an array of tuples containing two lambda expressions. The first determines whether the input matches against the current pattern; the second is the transformation in value that occurs if the pattern is a match. There’s a final check to see whether the default pattern should be applied—i.e., because none of the other patterns were a match.

Despite being a fraction of the length of the original code sample, this contains all the same functionality. The matching patterns on the lefthand side of the tuple are simple, but they can contain expressions as complicated as we’d like and could even be calls to whole functions containing detailed criteria to match on.

So, how do we make this work? This is an extremely simple version that provides most of the functionality required:

public static class ExtensionMethods
{
    public static TOutput Match<TInput, TOutput>(
        this TInput @this,
        params (Func<TInput, bool> IsMatch,
        Func<TInput, TOutput> Transform)[] matches)
    {
        var match = matches.FirstOrDefault(x => x.IsMatch(@this));
        var returnValue = match?.Transform(@this) ?? default;
        return returnValue;
    }
}

We use the LINQ method FirstOrDefault() to first iterate through the lefthand functions to find one that returns true (i.e., one with the right criteria), and then call the righthand conversion Func to get the modified value.

This is fine, except that if none of the patterns match, we’ll be in a bit of a fix. Most likely, we’ll have a null reference exception.

To cover this, we must force the need to provide a default match (the equivalent of a simple else statement, or the _ pattern match in switch expressions). The answer is to have the Match function return a placeholder object that either holds a transformed value from the Match expressions or executes the Default pattern lambda expression. The improved version looks like this:

public static MatchValueOrDefault<TInput, TOutput> Match<TInput, TOutput>(
  this TInput @this,
  params (Func<TInput, bool>,
  Func<TInput, TOutput>)[] predicates)
{
    var match = predicates.FirstOrDefault(x => x.Item1(@this));
    var returnValue = match?.Item2(@this);
    return new MatchValueOrDefault<TInput, TOutput>(returnValue, @this);
}

public class MatchValueOrDefault<TInput, TOutput>
{
    private readonly TOutput value;
    private readonly TInput originalValue;

    public MatchValueOrDefault(TOutput value, TInput originalValue)
    {
        this.value = value;
        this.originalValue = originalValue;
    }
}

public TOutput DefaultMatch(Func<TInput, TOutput> defaultMatch)
{
    if (EqualityComparer<TOutput>.Default.Equals(default, this.value))
    {
        return defaultMatch(this.originalValue);
    }
    else
    {
        return this.value;
    }
}

This approach is severely limited compared to what can be accomplished in the latest versions of C#. No object type matching occurs, and the syntax isn’t as elegant, but it’s still usable and could save an awful lot of boilerplate as well as encourage good code standards.

In versions of C# that are older still and that don’t include tuples, we can consider the use of KeyValuePair<T,T>, though the syntax is far from attractive. What, you don’t want to take my word? OK, here we go. Don’t say I didn’t warn you…​

The Extension() method itself is about the same and just needs a small alteration to use KeyValuePair instead of tuples:

public static MatchValueOrDefault<TInput, TOutput> Match<TInput, TOutput>(
  this TInput @this,
  params KeyValuePair<Func<TInput, bool>, Func<TInput, TOutput>>[] predicates)
{
    var match = predicates.FirstOrDefault(x => x.Key(@this));
    var returnValue = match.Value(@this);
    return new MatchValueOrDefault<TInput, TOutput>(returnValue, @this);
}

And here’s the ugly bit. The syntax for creating KeyValuePair objects is pretty awful:

var inputValue = 25000M;
var updatedValue = inputValue.Match(
    new KeyValuePair<Func<decimal, bool>, Func<decimal, decimal>>(
        x => x <= 12570, x => x),
    new KeyValuePair<Func<decimal, bool>, Func<decimal, decimal>>(
        x => x <= 50270, x => x * 0.8M),
    new KeyValuePair<Func<decimal, bool>, Func<decimal, decimal>>(
        x => x <= 150000, x => x * 0.6M)
).DefaultMatch(x => x * 0.55M);

So we can still have a form of pattern matching in C# 4, but I’m not sure how much we’re gaining by doing it. That’s perhaps up to you to decide. At least I’ve shown you the way.

Make Dictionaries More Useful

Functions don’t have to be used only for turning one form of data into another. We can also use them as filters, extra layers that sit between the developer and an original source of information or functionality. This section looks at a way of using functional filtering to improve the use of dictionaries.

One of my absolute favorite things in C# by far is dictionaries. Used appropriately, they can reduce a heap of ugly, boilerplate-riddled code with a few simple, elegant, array-like lookups. They’re also efficient to find data in, once created.

Dictionaries have a problem, however, that often makes it necessary to add in a heap of boilerplate that invalidates the whole reason they’re so lovely to use. Consider the following code sample:

var doctorLookup = new []
{
    ( 1, "William Hartnell" ),
    ( 2, "Patrick Troughton" ),
    ( 3, "Jon Pertwee" ),
    ( 4, "Tom Baker" )
}.ToDictionary(x => x.Item1, x => x.Item2);

var fifthDoctorInfo = $"The 5th Doctor was played by {doctorLookup[5]}";

What’s up with this code? It falls foul of a code feature of dictionaries that I find inexplicable: if you try looking up an entry that doesn’t exist,1 it will trigger an exception that has to be handled!

The only safe way to handle this is to use one of several techniques available in C# to check against the available keys before compiling the string, like this:

var doctorLookup = new []
{
    ( 1, "William Hartnell" ),
    ( 2, "Patrick Troughton" ),
    ( 3, "Jon Pertwee" ),
    ( 4, "Tom Baker" )
}.ToDictionary(x => x.Item1, x => x.Item2);

var fifthDoctorActor = doctorLookup.ContainsKey(5)
    ? doctorLookup[5]
    : "An Unknown Actor";

var fifthDoctorInfo = $"The 5th Doctor was played by {fifthDoctorActor}";

Alternatively, slightly newer versions of C# provide a TryGetValue() function to simplify this code a little:

var fifthDoctorActor = doctorLookup.TryGetValue(5, out string value)
    ? value
    : "An Unknown Actor";

So, can we use FP techniques to reduce our boilerplate code and give us all the useful features of dictionaries, but without the awful tendency to explode? You betcha!

First we need a quick extension method:

public static class ExtensionMethods
{
    public static Func<TKey, TValue> ToLookup<TKey,TValue>(
      this IDictionary<TKey,TValue> @this)
    {
        return x => @this.TryGetValue(x, out TValue? value) ? value : default;
    }

    public static Func<TKey, TValue> ToLookup<TKey,TValue>(
      this IDictionary<TKey,TValue> @this,
      TValue defaultVal)
    {
        return x => @this.ContainsKey(x) ? @this[x] : defaultVal;
    }
}

I’ll explain further in a minute, but first, here’s how we’d use the extension methods:

var doctorLookup = new []
{
    ( 1, "William Hartnell" ),
    ( 2, "Patrick Troughton" ),
    ( 3, "Jon Pertwee" ),
    ( 4, "Tom Baker" )
}.ToDictionary(x => x.Item1, x => x.Item2)
    .ToLookup("An Unknown Actor");

var fifthDoctorInfo = $"The 5th Doctor was played by {doctorLookup(5)}";
// output = "The 5th Doctor was played by An Unknown Actor"

Notice the difference? If you look carefully, the code is now using parentheses, rather than square array/dictionary brackets to access values from the dictionary. That’s because it’s technically not a dictionary anymore! It’s a function.

If you look at the extension methods, they return functions, but they’re functions that keep the original Dictionary object in scope for as long as they exist. Basically, they’re like a filter layer sitting between the Dictionary and the rest of the codebase. The functions make a decision on whether use of the Dictionary is safe.

It means we can use a Dictionary, but the exception that occurs when a key isn’t found will no longer be thrown, and we can either have the default for the type (usually null) returned, or supply our own default value. Simple.

The only downside to this method is that it’s no longer a Dictionary, in effect. We can’t modify it any further or perform any LINQ operations on it. If we are in a situation, though, where we’re sure we won’t need to, this is something we can use.

Parsing Values

Another common cause of noisy, boilerplate code is parsing values from string to other forms. We might use something like this for parsing in a hypothetical settings object, in the event we were working in .NET Framework and the appsettings.json and IOption<T> features aren’t available:

public Settings GetSettings()
{
    var settings = new Settings();

    var retriesString = ConfigurationManager.AppSettings["NumberOfRetries"];
    var retriesHasValue = int.TryParse(retriesString, out var retriesInt);
    if(retriesHasValue)
        settings.NumberOfRetries = retriesInt;
    else
        settings.NumberOfRetries = 5;

    var pollingHrStr = ConfigurationManager.AppSettings["HourToStartPollingAt"];
    var pollingHourHasValue = int.TryParse(pollingHrStr, out var pollingHourInt);
    if(pollingHourHasValue)
        settings.HourToStartPollingAt = pollingHourInt;
    else
        settings.HourToStartPollingAt = 0;

    var alertEmailStr = ConfigurationManager.AppSettings["AlertEmailAddress"];
    if(string.IsNullOrWhiteSpace(alertEmailStr))
        settings.AlertEmailAddress = "[email protected]";
    else
        settings.AlertEmailAddress = aea.ToString();

    var serverNameString = ConfigurationManager.AppSettings["ServerName"];
    if(string.IsNullOrWhiteSpace(serverNameString))
        settings.ServerName = "TestServer";
    else
        settings.ServerName = sn.ToString();

    return settings;
}

That’s a lot of code to do something simple, isn’t it? A lot of boilerplate code noise obscures the intention of the code to all but those familiar with these sorts of operations. Also, if a new setting were to be added, it would take five or six lines of new code for each and every one. That’s quite a waste.

Instead, we can do things a little more functionally and hide the structure away somewhere, leaving just the intent of the code visible for us to see.

As usual, here’s an extension method to take care of business:

public static class ExtensionMethods
{
    public static int ToIntOrDefault(this object @this, int defaultVal = 0) =>
        int.TryParse(@this?.ToString() ?? string.Empty, out var parsedValue)
            ? parsedValue
            : defaultVal;

    public static string ToStringOrDefault(
        this object @this,
        string defaultVal = "") =>
        string.IsNullOrWhiteSpace(@this?.ToString() ?? string.Empty)
            ? defaultVal
            : @this.ToString();
}

This takes away all the repetitive code from the first example and allows you to move to a more readable, result-driven code sample, like this:

public Settings GetSettings() =>
    new Settings
    {
        NumberOfRetries = ConfigurationManager.AppSettings["NumberOfRetries"]
            .ToIntOrDefault(5),
        HourToStartPollingAt =
            ConfigurationManager.AppSettings["HourToStartPollingAt"]
            .ToIntOrDefault(0),
        AlertEmailAddress = ConfigurationManager.AppSettings["AlertEmailAddress"]
             .ToStringOrDefault("[email protected]"),
        ServerName = ConfigurationManager.AppSettings["ServerName"]
            .ToStringOrDefault("TestServer"),

    };

It’s easy now to see at a glance what the code does, what the default values are, and how we’d add more settings with a single line of code. Any other settings value types besides int and string would require the creation of an additional extension method, but that’s no great hardship.

Custom Enumerations

Most of us have likely used enumerables when coding, but did you know that there’s an engine under the surface that we can access and use to create all sorts of interesting custom behaviors? With a custom iterator, we can drastically reduce the number of lines of code needed for more complicated behavior when looping through data.

First, though, it’s necessary to understand just how an enumerable works beneath the surface. A class sits beneath the surface of the enumerable, the engine that drives the enumeration, and this class allows us to use foreach to loop through values. It’s called the enumerator class.

The enumerator has two functions:

Current

This gets the current item out of the enumerable. This may be called as many times as we want, provided we don’t try moving to the next item. If we try getting the Current value before first calling MoveNext(), an exception is thrown.

MoveNext()

Moves from the current item and tries to see whether there is another to be selected. Returns true if another value is found, false if we’ve reached the end of the enumerable or there were no elements in the first place. The first time MoveNext() is called, it points the enumerator at the first element in the enumerable.

Query Adjacent Elements

Let’s start with a relatively simple example. Imagine that we want to run through an enumerable of integers, to see whether it contains any numbers that are consecutive. An imperative solution would likely look like this:

public IEnumerable<int> GenerateRandomNumbers()
{
    var rnd = new Random();
    var returnValue = new List<int>();
    for (var i = 0; i < 100; i++)
    {
        returnValue.Add(rnd.Next(1, 100));
    }
    return returnValue;
}

public bool ContainsConsecutiveNumbers(IEnumerable<int> data)
{
    // OK, you caught me out: OrderBy isn't strictly imperative, but
    // there's no way I'm going to write out a sorting algorithm out
    // here just to prove a point!
    var sortedData = data.OrderBy(x => x).ToArray();

    for (var i = 0; i < sortedData.Length - 1; i++)
    {
        if ((sortedData[i] + 1) == sortedData[i + 1])
            return true;
    }

    return false;
}

var result = ContainsConsecutiveNumbers(GenerateRandomNumbers());
Console.WriteLine(result);

To make this code functional, as is often the case, we need an extension method. This would take the enumerable, extract its enumerator, and control the customized behavior.

To avoid use of an imperative-style loop, we’ll use recursion here. Recursion (introduced in Chapters 1 and 2) is a way of implementing an indefinite loop by having a function call itself repeatedly.2

I’ll revisit the concept of recursion in Chapter 9. For now, let’s use the standard, simple version of recursion:

public static bool Any<T>(this IEnumerable<T> @this, Func<T, T, bool> evaluator)
{
    using var enumerator = @this.GetEnumerator();
    var hasElements = enumerator.MoveNext();
    return hasElements && Any(enumerator, evaluator, enumerator.Current);
}

private static bool Any<T>(IEnumerator<T> enumerator,
        Func<T, T, bool> evaluator,
        T previousElement)
{
    var moreItems = enumerator.MoveNext();
    return moreItems && (evaluator(previousElement, enumerator.Current)
        ? true
        : Any(enumerator, evaluator, enumerator.Current));

}

So, what’s happening here? This approach is kind of like juggling, in a way. We start by extracting the enumerator and moving to the first item.

Inside the private function, we accept the enumerator (now pointing to the first item), the “are we done” evaluator function, and a copy of that same first item.

Then we immediately move to the next item and run the evaluator function, passing in the first item and the new Current, so they can be compared.

At this point, either we find out we’ve run out of items or the evaluator returns true, in which case we can terminate the iteration. If MoveNext() returns true, we check if the previousValue and Current match our requirement (as specified by evaluator). If they do, we finish and return true; otherwise, we make a recursive call to check the rest of the values.

This is the updated version of the code to find consecutive numbers:

public IEnumerable<int> GenerateRandomNumbers()
{
    var rnd = new Random();

    var returnValue = Enumerable.Repeat(0, 100)
        .Select(x => rnd.Next(1, 100));
    return returnValue;
}

public bool ContainsConsecutiveNumbers(IEnumerable<int> data)
{
    var sortedData = data.OrderBy(x => x).ToArray();
    var result = sortedData.Any((prev, curr) => cur == prev + 1);
    return result;
}

It would also be easy enough to create an All() method based on the same logic, like so:

public static bool All<T>(
    this IEnumerator<T> enumerator,
    Func<T,T,bool> evaluator,
    T previousElement)
{
    var moreItems = enumerator.MoveNext();
    return moreItems
        ? evaluator(previousElement, enumerator.Current)
            ? All(enumerator, evaluator, enumerator.Current)
            : false
        : true;
}

public static bool All<T>(this IEnumerable<T> @this, Func<T,T,bool> evaluator)
{
    using var enumerator = @this.GetEnumerator();
    var hasElements = enumerator.MoveNext();
    return hasElements
        ? All(enumerator, evaluator, enumerator.Current)
        : true;
}

The only differences between All() and Any() are the conditions for deciding whether to continue and whether you need to return early. With All(), the point is to check every pair of values and return out of the loop early only if one is found not to meet the criteria.

Iterate Until a Condition Is Met

The technique described in this section is basically a replacement for a while loop, so there’s another statement we don’t necessarily need.

For this example, let’s imagine what the turn system might be like for a text-based adventure game. For younger readers, this is what we had in the old days, before graphics. You used to have to write what you wanted to do, and the game would write what happened—kind of like a book, except you wrote what happened yourself.

Note

Go and check out the epic adventure game Zork if you’d like to see this for yourself. Try not to get eaten by a grue!

The basic structure of one of those games was something like this:

  1. Write a description of the current location.

  2. Receive user input.

  3. Execute the requested command.

Here’s how imperative code might handle that situation:

var gameState = new State
{
    IsAlive = true,
    HitPoints = 100
};

while(gameState.IsAlive)
{
    var message = this.ComposeMessageToUser(gameState);
    var userInput = this.InteractWithUser(message);
    this.UpdateState(gameState, userInput);

    if(gameState.HitPoints <= 0)
        gameState.IsAlive = false;
}

In principle, what we want is a LINQ-style Aggregate() function, but one that doesn’t loop through all the elements of an array and then finish. Instead, we want the function to loop continuously until our end condition is met (the player is dead). I’m simplifying a little here (obviously, our player in a proper game could win as well). But my example game is like life, and life’s not fair!

The extension method for this is another place that would benefit from tail-recursion optimized calls, and I’ll be presenting options for that in Chapter 9. For now, though, we’ll just use simple recursion (which may become an issue if the game has a lot of turns) to avoid introducing too many ideas too soon:

public static class ExtensionMethods
{
    public static T AggregateUntil<T>(
      this T @this,
      Func<T,bool> endCondition,
      Func<T,T> update) =>
        endCondition(@this)
             ? @this
             : AggregateUntil(update(@this), endCondition, update);
}

Using this, we can do away with the while loop entirely and transform the entire turn sequence into a single function, like so:

var gameState = new State
{
    IsAlive = true,
    HitPoints = 100
};

var endState = gameState.AggregateUntil(
    x => x.HitPoints <= 0,
    x => {
        var message = this.ComposeMessageToUser(x);
        var userInput = this.InteractWithUser(message);
        return this.UpdateState(x, userInput);
    });

This isn’t perfect but it’s functional now. There are far better ways of handling the multiple steps to update the game’s state, and the issue of how to handle user interaction in a functional manner remains too. Chapter 13 covers those topics.

Summary

In this chapter, we looked at ways to use Func delegates, enumerables, and extension methods to extend C# to make it easier to write functional-style code and to get around a few existing limitations of the language. I’m certain that I’m barely scratching the surface with these techniques, and that plenty more are out there to be discovered and used.

The next chapter explores higher-order functions as well as some structures that can be used to take advantage of them to create yet more useful functionality.

1 Incidentally, it was Peter Davison.

2 An indefinite loop, but hopefully not infinite!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.36.32