Chapter 12. Existing Functional Programming Libraries in NuGet

It may, or more likely may not, surprise you to learn that I’m not the first person in the world to advocate for functional C#. Far from it. Quite a lot of people have done so before me, many of whom have even created handy libraries you can use to write functional-style code without making it all yourself.

Over the course of this chapter, I’ll talk about each of the libraries that exist in NuGet to provide functional features, how to use them, and what I think of them. Just be aware I didn’t make any of them. I just wrote this book.

A quick note first about the use of NuGet libraries like these. These aren’t necessarily the product of a big company that’s paying its employees to produce a shippable product to customers. Many of them are the work of passionate, talented developers who are doing this work in their spare time. This has both advantages and disadvantages if you’re considering using these packages in a production environment.

The big advantage is that you’ll honestly tend to get a better product in many cases than a company might manage. With no work politics, concerns over the needs of certain high-paying customers, paying staff or arbitrary deadlines, you can often get an amazing product fueled by pure passion.

The downside is that there’s no guarantee that these libraries will always be supported. Real life gets in the way sometimes, in ways that are unavoidable and impossible to predict. A one-person operation providing amazing work for us to use in C# might find themselves thinking again about the amount of work they do when they find themselves with a new addition to the family, an exciting new job that requires more of their available time, or even a lottery ticket that comes up as a winner on some sort of mega roll-over jackpot, giving them enough money to buy a private island in the tropics. The code might well continue to run for many years, but sooner or later a change may come to a new release of .NET five years from now that makes the library less than usable.

I’m not saying anything of that sort will happen, but bear it in mind when deciding whether to integrate a package’s features into your codebase. Have a look at the relevant GitHub pages and the level of support the package is getting. Try to determine whether it has some form of corporate sponsorship, or at least whether an active team of people is supporting the product and a community exists around them.

Open source projects that die can even be resurrected if there’s enough community support. Another advantage of being open source is that you can take the source code yourself, and maintain your own version in the event of there being no further official support.

To put things in perspective, Vue.js started life as a one-man open source operation. It’s now used extensively in production around the world.

I can’t ultimately advise you on whether you should choose an open source project from NuGet or create your own functional tools. That’s a decision that has to be made by you, your team (if there is one), and your organization (if you belong to one), and must be done bearing in mind the unique constraints of whatever project you’re working on.

I’ve tried my best to select projects in this chapter that are well supported, and owned by people who care about their product and the community. The open source world changes rapidly, however, so I’d still advise performing your own investigation to ensure that nothing has changed since I wrote this.

OneOf

The OneOf library is owned by Harry McIntyre. It seems to be one of the better-known functional libraries out there, and folks I’ve talked to have recommended this one more than any other.

Its stated intention is to provide “F#-like” DUs in C#. To give them a test run, I’ve redeveloped all my code samples from Chapter 6 using OneOf to see how it compares.

First, the CustomerOffering union type looks like this:

var customerOffering = new OneOf<Holiday, HolidayWithMeals, DayTrip>();

Note here that there is no abstract base class. With this library, all the DUs we create are of the type OneOf<T, T, T> or however many types we attach to the union.1 This makes it easier to declare unions on the fly, without needing to create the additional code infrastructure required by my method.

Since there’s no abstract base class, however, it’s necessary instead to use the built-in OneOf Match() function to collapse the union down to a concrete type. This is how our sample from Chapter 6 determining which format method to call would look:

public string formatCustomerOffering(
    OneOf<Holiday, HolidayWithMeals, DayTrip> c) =>
    c.Match(
        x => this.formatHoliday(x),
        x => formatHolidayWithMean(x),
        x => formatDayTrip(x)
    );

This code is nice and neat, but you should bear a few points in mind when comparing it to our version:

  • There’s no default match, as when using a switch expression, so every single member type of the union has to have a switch expression matched. This may not be a problem if we want to ensure that every possible member of the union has a specific handler.

  • Use of the switch expression when keyword to examine the values of the properties of the type we’re examining isn’t possible. That logic would have to be written into the Match function somehow.

  • Each match type has to have the same return value, unless you convert to yet another union type! This may not be an issue if you’re constructing some sort of feedback message to the end user, however.

To get a concrete, real object out of the union type, we can use a set of functions to check the real type and then convert it over:

var offering = GetCustomerOffering();
if(offering.IsT0)
{
    var holiday = offering.AsT0;
    // do something with the holiday type
}
if(offering.IsT1)
{
    var holidayWithMeals = offering.AsT1;
    // do something with the HolidayWithMeals type
}
if(offering.IsT2)
{
    var dayTrip = offering.AsT2;
    // do something with the DayTrip type
}

Note that this is being handled generically, so the types are simply referred to as T1, T2, etc. The order they’re listed in the definition of the DU determines which number corresponds to which type.

This library has a whole load of built-in classes too, which are there to use in unions. They have names like Some, None, Yes, No, and Maybe. Despite the names, these can’t be used to create a Something<T> class as we did in Chapter 7; they’re just handy types to use when there is no return value and we’d like to return something descriptive to the outside world.

To create a true Maybe with OneOf, we’d need to define our own:

public class Something<T>
{
    public T Value { get; set; }
}

public OneOf<Something<Holiday>,None> GetHoliday() { }

The None class here is out of the OneOf library. It’s only purpose is to signify that no data was found, so it’s perfectly usable here. No Bind() or Map() functions are associated with the OneOf classes, which are purely to be used as DUs; it’s not possible to use an instance of OneOf as a monad.

Nothing is stopping you from writing your own Bind() to extend a OneOf DU into a true monad, however:

public static OneOf<Something<TOut>, None> Bind<TIn, TOut>(
  this OneOf<Something<TIn>, None> @this,
  Func<TIn, TOut> f)
{
    if (!@this.IsT0) return new None();
    var sth = @this.AsT0;
    var returnValue = f(sth.Value);
    return new Something<TOut>
    {
        Value = returnValue
    };
}

OneOf is certainly a nice, easy-to-use, lightweight library. If you feel that the loss in flexibility compared to creating your own custom unions is acceptable, it’s a worthwhile addition to your codebase. I’d certainly have no issues if someone asked to include it on a project I was working on.

One thing to bear in mind is that you can create DUs like a Maybe with OneOf, but you can’t make it a monad—there are no implementations of the Map() or Bind() functions. If you really want the monad functionality, you might need to look into other libraries or make your own.

LanguageExt

Owned by Paul Louth, and found in NuGet as LanguageExt.Core, this library is an attempt to implement as much of the functional paradigm as is possible in C#.

LanguageExt is a very large library of classes, static functions, and other features. I can’t hope to describe all of it here, as that would probably fill an entire chapter! I’ll restrict myself to just presenting the features that match functional content I’ve covered here already, and hopefully give a flavor of how LanguageExt is to use.

Before I begin, most of the functions and types required are part of a large partial class called Prelude. It might be worth adding this line to your global using statements file:

using static LanguageExt.Prelude;

Once that’s done, it should be possible to construct most monads and other structures with a simple function call.

Option

Maybes are available in LanguageExt, though they’re called Options. Another difference in approach is that for instantiating objects of that kind, static methods are defined for the purpose.

This is what my MakeGreeting() function looks like in LanguageExt:

public static Option<string> MakeGreeting(int employeeId) =>
    employeeId.Bind(empRepo.GetById)
        .Map(x => "Hello " + x.Salutation + " " + x.Name);

There are a few things to explain here. Strictly speaking, there is a difference between Map() and Bind(). They’re often used interchangeably, but there is a difference.

Bind() takes a normal C# object or primitive and passes it to a function that returns an “elevated” value—i.e., instead of an int, it returns Maybe<int> or something of that sort.

Map() attaches to an “elevated” value (e.g., Maybe<int>) and allows you to pass the value inside it to another function without having to worry about unwrapping the elevated value. This is pretty much the same as the Bind() function used in Chapter 7.

In the preceding code sample, we start with a non-elevated int and use Bind() to pass it to the employee repository, which returns a type of Maybe<Employee>. The Map() function that follows just sees the Employee object that may have been returned by the GetById() function. In a process going on under the surface, Maybe<int> is unwrapped to int; then, once the arrow function we’ve passed in has executed, its string return value is wrapped into a Maybe<string>.

It doesn’t help that Map() is also used in some programming languages like JavaScript to describe the sort of operation that’s applied to arrays like Select() that operate element by element. Map() is also used in Jimmy Bogard’s AutoMapper library, which is another reason to carefully consider using the term in C#. The term Map is heavily overloaded in the programming world, which is why I tend to shy away from it.

You can’t use null, incidentally, with LanguageExt. It uses its own None type to denote the lack of a solid return value. This way, returning “nothing” is a choice made by the developer, rather than relying on the built-in default value of a type.

You might define functions by using Some and None like this:

public Option<Employee> GetById(int id)
{
    try
    {
        var emp = this.DbConnection.GetFromDb(id);
        return emp == null
            ? Option<Employee>.None
            : Option<Employee>.Some(emp);
    }
    catch(Exception e)
    {
        return Option<Employee>.None
    }
}

Collapsing an Option to a real value doesn’t appear to be possible with the use of a C# switch expression. Instead, LanguageExt requires the use of a Match() function, to which we pass in two arrow functions—one to be executed in the event of Some, and the other in the event of None:

var result = empRepo.GetById(10);
var message = result.Match(s => "Hi there, " + s.Name,
    () => "I don't know who you are, but Hi all the same");

LanguageExt also contains a rich set of functions that can be applied to elevated values like Maybe. Here are a few examples:

Iter()

A kind of Tap() to perform a read-only action in the event of the Option currently being a Some. Slightly more descriptive alternatives are also available: ifSome() and IfNone().

MatchUnsafe()

Like a normal Match(), but also allows null to be returned, in the event that it matters to you.

Fold()

An aggregation function that is like Aggregate() in LINQ but that is aware of the Option and won’t try to operate on None types.

Filter()

Like Where() in LINQ, but aware of the Option type.

Either

The Either monad is available in LanguageExt as well. Its typical usage of this is to have the left as the “unhappy” path, containing error details, but in fact you can do anything you’d like with the two forms. This effectively means you can use it as a DU as well.

Here’s a LanguageExt version of the name-formatting function from Chapter 6:

public string formatName(Either<ChineseName, BritishName> n) =>
    n.Match(
        bn => bn.Honorific + " " + bn.FirstName + " " +
            string.Join(" ", bn.MiddleNames) + " " + bn.LastName,
        cn => cn.FamilyName + " " + cn.GivenName + " " +
            cn.Honorific + " "" + cn.CourtesyName + """);

Note also that when using this method, there’s no need for an inheritance connection to exist between the two classes, although the only way to use a switch expression would be if we referenced the classes as simply Object, since there’s nothing else tying them together, but then the LanguageExt Match() function is a replacement for the switch statement. The library has no built-in support for matching on properties and subproperties without switch expressions, though.

As with all things, whether to use a library like LanguageExt depends on what you’re trying to accomplish and the constraints of your project.

Memoization

LanguageExt has a class and a set of extension methods to implement memoization. Unfortunately, only Func delegates with either a single parameter or no parameters at all can be memoized, so you may have to write your own extension method for LanguageExt itself if you have memoized functions with multiple parameters.

This is our Bacon number example from Chapter 10 with LanguageExt swapped in for our version. As you can see, it’s pretty much a straight replacement:

var getCastForFilm((Film x) => this.castRepo.GetCast(x.Filmid);
var getCastForFilmM = getCastForFilm.Memo(x => x.Id.ToString());

Here’s a simple example to prove this implementation of memoization works in principle. We’re using xUnit for the unit-test framework and Fluent Assertions to provide assertions:

[Fact]
public void memoized_functions_should_not_call_again_with_the_same_parameter()
{
    var timesCalled = 0;
    var add10 = (int x) => { timesCalled++; return x * 10; };
    var add10M = add10.Memo();

    add10M(1);
    add10M(2);
    add10M(1);

    timesCalled.Should().Be(2);
}

In the preceding example, add10M() is called three times, but the add10 delegate that sits behind it is called only twice, because the value returned by a parameter value of 1 is cached, and the stale cached version is returned instead.

I added a bit of code inside the add10() function to record the number calls to it. It’s really super nonfunctional, but don’t hold it against me. I wouldn’t do that sort of thing in production!

Reader

The implementation of Reader in LanguageExt is probably slightly neater than ours. LanguageExt uses a delegate type to allow a function to be directly converted into a Reader, and an object containing a ton of helper functions called Prelude that we can make a global static, making it a little easier than our version (see Chapter 7) with extension methods:

using static LanguageExt.Prelude;

var reader = Reader<int, int>(e => e * 100)
    .Map(x => x / 50)
    .Map(x => x.ToString());

var result = reader.Run(100);

As you can see, the use of the LanguageExt implementation of the Reader monad is similar to our version, but with a few nice touches to make it a little easier on the eyes.

State

The last feature of LanguageExt I want to present (there are many more, I’m just sticking to what I see as the main ones) is the State monad (see Chapter 7 for our version).

This monad has a little more boilerplate as compared to ours, but it’s largely the same:

var result1 = State<int, int>(x => (10, x))
    .Bind(x => State<int, int>(s => (x * s, s)))
    .Bind(x => State<int, int>(s => (x - s, s)))
    .Bind(x => State<int, int>(s => (x, s - 5)))
    .Bind(x => State<int, int>(s => (x / 5, s)));

var (finalValue, finalState, _) = result1(10);
// finalValue = 18
// finalState = 5

That’ll do with LanguageExt features for now. Hopefully, this whistle-stop tour has given you an idea of its capabilities.

LanguageExt Wrap-up

Overall, LanguageExt is a deep, rich functional library with pretty much all the features you’re likely to want in order to implement as much of the functional paradigm as is possible in C#.

I’m also only scratching the surface in terms of the features available in LanguageExt. There are also, among others, libraries included for fluent assertions in unit testing and more besides.

Should you use LanguageExt in production? That’s entirely up to you. I don’t find it’s much effort to implement the functional paradigm in C#, with only a few lines of code required in most cases. That also means I can stick to whatever style of syntax suits me best. As a consequence, I don’t use LanguageExt myself, but having said that, there’s no special reason not to. It’s a lovely, well-designed library.

Consider also the consequences of adopting such a large, extensive library of features across your codebase. I’m hoping it’s around for many years to come, as it appears to be popular, but in case it ever isn’t one day, that may cause a real problem if you rely on it heavily.

Either way, it’s up to you, and your own personal risk/benefit ratio calculation. Let me know how you get on if you do use it!

Functional.Maybe

The Functional.Maybe library is currently maintained by Andrey Tsvetkov, based on a now-abandoned project called Data.Maybe by William Casarin.

This project is a fairly lightweight implementation of the Maybe and Either monads, and may well be of interest if those are the only bits of the functional paradigm you’re wanting to inject into your codebase.

Rather than implementing the Maybe monad as a DU via an abstract base class, this library implements it as a readonly struct with a bool property that can be used to determine whether there is a value inside the Maybe.

Here’s an implementation of a Fahrenheit-to-Celsius temperature conversion function using Functional.Maybe:

public string FarenheitToCelsius(decimal input) =>
input.ToMaybe()
    .Select(x => x - 32)
    .Select(x => x * 5)
    .Select(x => x / 9)
    .Select(x => Math.Round(x, 2))
    .Select(x => x.ToString())
    .Value;

To better tie in with the existing .NET LINQ syntax, the Bind() function is called Select() here. Whether this is a confusing overload of an existing term or a comforting use of a familiar term is a decision I leave to you.

This is how you’d use the Maybe as the return type from a function that gets data from an external source—to represent the uncertainty of any data being received back:

public Maybe<Employee> GetById(int id)
{
    try
    {
        var e =  this.DbConnection.GetFromDb(id);
        return e.ToMaybe();
    }
    catch (Exception e)
    {
        return Maybe<Employee>.Nothing;
    }
}

The code is pretty straightforward. You don’t necessarily have to use the ToMaybe() function; Maybe<T> has a constructor, but I find this syntax cleaner.

This method doesn’t use inheritance, and there is no Match() extension method, so the way to move from the Maybe to a real, concrete value is to check for a value and then return whatever you want based on it:

var e = this.EmployeeRepo.GetById(24601);
var message = e.HasValue
    ? "Hello " + e.Value.Name
    : "I don't believe we've met, have we?";

A few extra bits of functional theory are included with the version of Maybe in this library. A Tap() function is also available, here called Do(). The Do() function executes only if the source Maybe has a value, so it’s the equivalent of my OnSomething() function:

var taxData = this.EmployeeRepo.GetById(24601)
    .Do(x => this.logger.LogInfo("got employee " + x.Id)
    .Select(x => this.payrollRepo.GetTaxInfo(x.TaxId));
    .Do(x => "Got his tax too!");

In addition, a nifty Or() function acts like an alt combinator (see “Alt Combinator”), but with the monad flow feature integrated into it.

One big difference from our version is that the parameter set has no params list of functions and also requires that every function provided to the alt combinator has no parameters and returns a Maybe type. The James Bond function from Chapter 5 might work, provided each lookup function returns something like a Maybe<SecretAgent>, like this:

var jbId = "007";
var jamesBond = this.hotelService.ScanGuestsForSpies(jbId)
    .Or(() => this.airportService.CheckPassengersForSpies(jbId))
    .Or(() => this.barService.CheckGutterForDrunkSpies(jbId));

    if(jamesBond.HasValue)
        this.deathTrapService.CauseHorribleDeath(jamesBond);

This code requires a few extra hoops to jump through, but it’s still usable.

Also worthy of mention is that the library comes with an extension method for one of my favorite C# features: the dictionary. Instead of trying to get a key, you can get a Maybe from it instead. I like this idea very much.

CSharpFunctionalExtensions

The CSharpFunctionalExtensions library is maintained by Vladimir Khorikov as an extension of his Pluralsight course “Applying Functional Principles in C# 6”.

The name of the course aside, as of the time of writing, the library supports all versions of .NET up to .NET 6.0, and updates have occurred within the last few months, so it would appear that Khorikov is keeping up with the latest .NET developments. My experiments with the library were all with .NET 7.0, in any case, and everything worked just fine.

Maybe

I tried the same Maybe-driven MakeGreeting function that I’ve used elsewhere in this chapter, but CSharpFunctionalExtensions supports only Maybes with nullable data types, so an int simply wasn’t possible as a parameter value.

The easy fix is to make the employeeId variable nullable. Alternative solutions could have included making a complex parameter class with potentially many parameters or making the int a Maybe<int> instead. This will do for the purposes of demonstration, though.

Here’s the code we’d end up with:

public static Maybe<string> MakeGreeting(int? employeeId) =>
    employeeId.AsMaybe().Map(empRepo.GetById)
        .Map(x => "Hello " + x.Salutation + " " + x.Name);

As with many other libraries I’ve looked at, there’s no option to use a switch expression to collapse the Maybe down to a concrete value; instead, some built-in helper functions allow us to do the job:

var martyMcFly = MakeGreeting(1985);
var messageToUser = martyMcFly.HasValue
    ? martyMcFly.Value
    : "Intruder Alert!";

Alternatively, we can use a Match() function:

var martyMcFly = MakeGreeting(1985);
var messageToUser = martyMcFly.Match(x => "Success: " + x,
    () => "Intruder Alert!");

Nothing really is wrong with this. There are only two possible values, so it’s a perfectly decent way of handling the Something/Nothing scenario.

It has built-in features for handling async functions as well, and some functions for applying logical operations to the contents of the Maybe, like this:

public static Maybe<string> MakeMessage(int? employeeId) =>
    employeeId.AsMaybe().Map(empRepo.GetById)
        .Where(x => !x.Interests.Contains("Homer Simpson"))
        .Map(x => "Welcome, " + x.Name + "!")
            .Or(() => "This is the No Homers Club, be off with you!!");

I quite like the Where() and Or() methods, and I can’t promise I won’t be using a version of them in my own code at some point!

Result

Result is similar in some ways to the Either monad I mentioned back in Chapter 7. Like a Something, it holds a value, but beyond that, it can also exist in one of two forms: Success or Failure. The difference between this and the Maybe is that either way it holds the value.

The two classes in this union type are used for information purposes—do we consider this operation a success or not. The library contains a set of fluent-style functions we can use to define whether we think the end result should be a success.

Here’s an example:

public static Maybe<string> MakeMessage(int? employeeId) =>
    employeeId.AsMaybe()
       .Map(empRepo.GetById)
       .ToResult("Could not find the employee")
       .Ensure(x => x.Interests.Contains("Doctor Who"), "You don't like DW!")
       .Ensure(x => x.Name != "Homer Simpson",
        "I keep telling you Homer, this is our tree house!")
       .Tap(x => Logger.LogInformation("Processing " + x.Name))
       .Finally(
        x => x.IsSuccess
        ? "Welcome to the No Homers Club, " + x.Value.Name
           : "Couldn't sign up " + x.Value.Name + ": " + x.Error);

The Ensure() function here will return a Result<T> that’s a Success if its parameter function returns true; otherwise, it’s a Failure.

As with a Maybe, once a result is in the Failure state, any subsequent Ensure() calls will not be executed.

The Finally() function will be executed at the end, regardless of Success or Failure, and can be used to collapse Result into a real value.

Fluent Assertions

One of my favorite libraries in NuGet is Fluent Assertions. It’s used to write asserts for unit tests that are something closer to natural language. Not just that, but it’s an incredibly rich library, containing all sorts of nifty assert types to make the job of writing unit tests easier.

CSharpFunctionalExtensions can also be further extended with another library: CSharpFunctionalExtensions.FluentAssertions, which adds more asserts to Fluent Assertions, specifically for use with this functional library. If you’ve decided to use CSharpFunctionalExtensions, I’d make sure you include that library as well.

CSharpFunctionalExtensions Wrap-up

The CSharpFunctionalExtensions library isn’t as definitive as LanguageExt; it contains only two structures. However, the design of those structures is really quite lovely. The expressive function names and the ability to use a fluent-style interface to write a detailed piece of functionality make this library well worth your consideration.

The F# Programming Language

Oh, come on. You’re just being silly now!

This said, it’s not a NuGet package, but you can reference .NET projects written in F# from C# projects, so…​yes, I suppose so. This topic is well outside the scope of this book, however.

Summary

Quite a few libraries are available already in NuGet if you want the initial creation of functional structures handled for you. These range from the fully comprehensive (LanguageExt) to those that just supply a few functional features each (OneOf, Functional.Maybe, etc.).

Whether you want to use some, all, or none of these libraries is entirely up to you.

Using none of them will mean you’ll have to develop your own functional classes and extension methods. This is a little more work, but means you won’t be dependent upon a third-party library, and you’ll be able to customize your classes and methods to work however you’d like.

Using a library will save you that effort, but force you to work to the style required by that library. You’ll also be dependent on that library’s development team to continue to produce updates.

As with all decisions, you need to consider the risks and benefits, weigh them, and see which way the scales tilt.

I can’t advise you on this, but I’ll be honest—I don’t use a library. I have my own custom functional library that I port between projects—either in part or in its entirety, depending on what I need to do. Having said that, there’s nothing wrong with using one. The libraries I’ve listed in this chapter are excellent pieces of work, and no doubt will be invaluable to you if you choose to use them.

In the next chapter, I’m going to put everything together into a game you can write and play yourself—entirely in functional-style C#. Have fun!

1 Up to a maximum of 32, in actual fact.

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

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