Chapter 7. Functional Flow

Calls to external systems such as databases and web APIs are an absolute pain, aren’t they? Before using your data—the most important part of the function you’re writing—you have to do the following:

  1. Catch and handle any exceptions. Maybe the network was glitching, or the database server was offline?

  2. Check that what has come back from the database is not null.

  3. Check that there’s an actual, reasonable set of data, even if it isn’t null.

This entails a lot of tedious boilerplate, which gets in the way of your business logic.

Use of the Maybe DU from the previous chapter will help somewhat with returning something other than null for records not found or errors encountered, but boilerplate is still required even then.

What if I were to tell you there is a way to never have to see another unhandled exception again? Not only that, but you’d never even need to use a try/catch block again. As for null checks? Forget ’em. You won’t ever have to do that again either.

Don’t believe me? Well, strap in; I’m going to introduce you to one of my favorite FP features. I use it all the time in my day job, and I’m hoping that after reading this chapter, you might too.

Maybe, Revisited

I’d like to revisit that Maybe DU from Chapter 6 now, but this time I’m going to show you how it can be even more useful than you could ever have imagined.

What I’m going to do is walk you through adding in a version of the Map() extension method from a couple of chapters ago. As you may remember, the Map() combinator in “Chaining Functions” works similarly to the LINQ Select() method, except it acts on the entire source object, not individual elements from it.

This time, though, we’re going to add a bit of logic inside Map(), something that will determine which type is going to come out. We’ll give it a different name this time—Bind():1

public static Maybe<TOut> Bind<TIn, TOut>(
    this Maybe<TIn> @this,
    Func<TIn, TOut> f)
{
    try
    {
        Maybe<TOut> updatedValue = @this switch
        {
            Something<TIn> s when !EqualityComparer<TIn>.Default.Equals(
                s.Value, default) =>
                    new Something<TOut>(f(s.Value)),
            Something<TIn> _ => new Nothing<TOut>(),
            Nothing<TIn> _ => new Nothing<TOut>(),
            Error<TIn> e => new Error<TOut>(e.ErrorMessage),
            _ => new Error<TOut>(new Exception(
                "New Maybe state that isn't coded for!: " +
                @this.GetType()))
        };
        return updatedValue;
    }
    catch (Exception e)
    {
        return new Error<TOut>(e);
    }
}

So what’s happening here? One of a few possible things:

  • The current value of This (the current object held by Maybe) is a Something—i.e., an actual nondefault valued object or primitive,2 in which case the supplied function is executed and whatever comes from it is returned in a new Something.

  • The current value of This is a Something, but the value inside it is the default (null, in most cases), in which case instead of Something, we now return a Nothing.

  • The current value of This is a Nothing, in which case we return another Nothing. No point doing anything else.

  • The current value of This is an error. Again, there’s no point doing anything except passing it on.

What’s the point of all this? Well, imagine the following procedural code:

public string MakeGreeting(int employeeId)
{
    try
    {
        var e = this.empRepo.GetById(employeeId);
        if(e != null)
        {
            return "Hello " + e.Salutation + " " + e.Name
        }

        return "Employee not found";
    }

    catch(Exception e)
    {
        return "An error occurred: " + e.Message;
    }
}

When you look at it, the purpose of this code is incredibly simple: fetch an employee; and if there are no issues, say hello to them. But, since null and unhandled exceptions are a thing, we have to write so much defensive code—null checks and try/catch blocks—not just here, but all over our codebase.

What’s worse is that we make it the problem of the code calling this function to know what to do with it. How do we signal that an error occurred or that the employee wasn’t found? In this example, we just return a string for whatever application we’ve written to display blindly. The other option would be to return some sort of return object with metadata attached (e.g., bool DataFound, bool ExceptionOccurred, exception CapturedException).

By using a Maybe and Bind() function, none of that is necessary. We could rewrite the code like this:

public Maybe<string> MakeGreeting(int employeeId) =>
    new Something(employeeId)
        .Bind(x => this.empRepo.GetById(x))
        .Bind(x => "Hello " + x.Salutation + " " + x.Name);

Think about the possible results for each bind listed here.

If the employee repository returns a null value, the next Bind() call will identify a Something with a default (null) value, and not execute the function that constructs a greeting string; instead it will return Nothing.

If an error occurs in the repository (maybe a network connection issue, something impossible to predict or prevent), the error will simply be passed on instead of the function executing.

The ultimate point I’m making is that the arrow function that assembles a greeting will execute only if the previous step (a) returns an actual value and (b) doesn’t throw an unhandled exception. This means that the small function written with use of the Bind() method is functionally identical to the previous version, covered with defensive code.

It gets better…​

We’re not returning a string anymore; we’re returning a Maybe<string>. This is a DU that can be used to inform whatever is calling our function of the result of the execution, whether it worked, and so on. That can be used in the outside world to decide how to handle the resulting value, or it can be used in subsequent chains of Bind() calls.

We can use the DU like this:

public Interface IUserInterface
{
    void WriteMessage(string s);
}

// Bit of magic here because it doesn't matter
this.UserInterface = Factory.MakeUserInterface();

var message = makeGreetingResult switch
{
    Something s => s.Value,
    Nothing _ => "Hi, but I've never heard of you.",
    Error _ => "An error occurred, try again"
};

this.UserInterface.WriteMessage(message);

Alternatively, we could adapt the UserInterface module so that it takes Maybe as a parameter:

public Interface IUserInterface
{
    void WriteMessage(Maybe<string> s);
}

// Bit of magic here because it doesn't matter
this.UserInterface = Factory.MakeUserInterface();

var logonMessage = MakeGreeting(employeeId)
    .Bind(x => x + Environment.NewLine + MakeUserInfo(employeeId));
this.UserInterface.WriteMessage(logonMessage);

Swapping out a concrete value in an Interface for a Maybe<T> is a sign to the class that consumes it that there is no certainty that the operation will work, and forces the consuming class to consider each of the possibilities and how to handle them. It also puts the onus on deciding how to respond entirely in the hands of the consuming class. There’s no need for the class returning the Maybe to have any interest in what happens next.

The best description I’ve ever encountered for this style of programming was in a talk and related articles by Scott Wlaschin called “Railway Oriented Programming”. Wlaschin describes the process as being like a railway line with a series of points. Each set of points is a Bind() call. The train starts on the Something line, and every time a function passed to a Bind() is executed, the train either carries on to the next set of points or switches to the Nothing path, and simply glides along to the station at the end of the line without doing any more work.

It’s a beautiful, elegant way of writing code that cuts out so much boilerplate it isn’t even funny. If only there were a handy technical term for this structure. Oh wait, there is! It’s called a monad!

I said way back at the beginning of the book that they might pop up somewhere. If anyone ever says to you that monads are complicated, I hope you can see now that they’re wrong.

Monads are like a wrapper around a value of some kind—like an envelope or a burrito. They hold the value but make no comment about what it’s actually set to. What they do is give us the ability to hang functions off them that provide a safe environment to perform operations without having to worry about negative consequences—such as null reference exceptions.

The Bind() function is like a relay race; each call does some sort of operation and then passes its value to the next runner. Bind() also handles errors and nulls for us, so we don’t need to worry about writing so much defensive code.

If you like, imagine a monad being like an explosion-proof box. You have a package you want to open but don’t know whether it’s something safe like a letter3 or something explosive just waiting to take you down when you lift the lid. If you pop the package inside the monad container, the package can open safely or explode, but the monad will keep you safe from the consequences.

That’s really all there is too it. Well, mostly.

In the rest of this chapter, I’ll show you what else we can do with monads, and what other sorts of monads there are. Don’t worry, though. The “hard” part is over now; if you’ve reached this point and you’re still with me, the rest of this book is going to be a piece of cake.4

Maybe and Debugging

A comment I hear sometimes regarding strings of Bind() statements is that they make it harder to use debug tools in Visual Studio to step through the changes—especially when you have scenarios like this:

var returnValue = idValue.ToMaybe()
    .Bind(transformationOne)
    .Bind(transformationTwo)
    .Bind(transformationThree);

Stepping through the changes is actually possible in most versions of Visual Studio, but you’d need make sure you keep mashing the F11 “Step-in” key on the keyboard to enter the nested arrow functions inside the Bind() call. This approach is still hardly the best for working out what’s happening if a value isn’t being calculated correctly. It’s even worse when you consider Step-in will enter the Maybe’s Bind() function and need a few more steps to be taken before seeing the result of the arrow function.

I tend to write my Bind() functions one to a line, each storing a variable containing its individual output:

var idMaybe = idValue.ToMaybe();
var transOne = idMaybe.Bind(x => transformationOne(x));
var transTwo = transOne.Bind(x => transformationTwo(x));
var returnValue = transTwo.Bind(x => transformationThree(x));

Functionally, these two samples are identical. It’s just that we’re capturing each output separately rather than immediately feeding it into another function and discarding them.

The second sample is easier to diagnose issues with, though, as we can inspect each intermediate value. Debugging is made easier because of the FP technique of never modifying a variable after it’s set. This means that every intermediate step in the process is set in stone to work through what exactly happened, as well as how and where things went wrong in the event of a bug.

These intermediate values will remain in scope for the entirety of the life of whatever larger function they’re part of, though. If it’s an especially large function, and one of the intermediate values is hefty, merging them might be worthwhile so that the large intermediate value is descoped as early as possible.

This decision is mostly a matter of personal style and one or two codebase constraints. Whichever you choose is fine.

Map() Versus Bind()

Strictly speaking, the preceding code is not implementing the Bind() function in accordance with the functional paradigm. Two functions should be attached to the Maybe: Map() and Bind(). They’re nearly the same, but with a small and subtle difference.

The Map() function is like the one I described in the previous section—it attaches to a Maybe<T1> and needs a function that gives you the value of type T1 from inside the Maybe and requires you to turn it into a type T2.

An actual Bind() needs you to pass in a function that returns a Maybe of the new type—i.e., Maybe<T2>. It still returns the same result as the Map() function:

public static Maybe<TOut> Map<TIn, TOut>(
    this Maybe<TIn> @this,
    Func<TIn, TOut> f) => // Some implementation

public static Maybe<TOut> Bind<TIn, TOut>(
    this Maybe<TIn> @this,
    Func<TIn, Maybe<TOut>> f) => // Some Implementation

If, for example, we have a function that calls a database and returns a type of Maybe<IEnumerable<Customer>> to represent a list of customers that may or may not have been found—then we call that with a Bind() function.

Any subsequent chained functions to change the IEnumerable of customers into another form would be done with a Map() call, since those changes are data-to-data, not data-to-maybe.

Here’s how we might go about implementing a proper Bind():

public static Maybe<TOut> Bind<TIn, TOut>(
    this Maybe<TIn> @this,
    Func<TIn, Maybe<TOut>> f)
{
    try
    {
        var returnValue = @this switch
        {
            Something<TIn> s => f(s.Value),
            _ => new Nothing<TOut>()
        };
        return returnValue;
    }
    catch (Exception _)
    {
        return new Nothing<TOut>();
    }
}

And here’s an example of how to use the function:

public Interface CustomerDataRepo
{
    Maybe<Customer> GetCustomerById(int customerId);
}

public string DescribeCustomer(int customerId) =>
    new Something<int>(customerId)
        .Bind(x => this.customerDataRepo.GetCustomerById(x))
        .Map(x => "Hello " + x.Name);

Use this new Bind() function and rename the previous one Map(), and you’re conforming to the functional paradigm a little closer.

In production code, however, I don’t personally do this. I just use a function called Bind() for both purposes. Why, you might ask? It’s mostly to prevent confusion, in all honesty. JavaScript has a native Map() function, but it operates like a C# Select() on individual elements of arrays. C# also has a Map() function in Jimmy Bogard’s AutoMapper library, which is used to convert an array of objects from one type to another.5

With both of these instances of a Map() function already in use in many C# code­ba⁠ses, I thought adding another Map() into the mix might confuse other folks looking at my code. For this reason, I use Bind() for all purposes, as C# and JavaScript don’t have a Bind() function—except in libraries implementing the functional paradigm.

You can choose whether to use the more strictly accurate version with both Map() and Bind(), or the route that—in my opinion—is less confusing and more pragmatic, which is to simply use multiple instances of the Bind() function to serve every purpose. I’m going to carry on assuming that second option for the rest of this book.

Maybe and the Primitives

Maybe and the Primitives sounds like the title of an amazing pulp adventure novel that was never written. It would probably involve our heroine, Captain Maybe, swinging to the rescue somewhere in a lost civilization populated by aggressive cave-dwelling nasties.

In fact, a primitive type in C# is one of a set of built-in types that don’t default to null:

  • bool

  • byte

  • sbyte

  • char

  • decimal

  • double

  • float

  • int

  • uint

  • nint

  • nuint

  • long

  • ulong6

  • short

  • ushort

The point here is that if we were to use any of these on a Bind() function from the previous sections and set their value to 0, it would fall foul of the check against default, because most of these default to 0.7

Here’s an example of a unit test that would fail (I’m using xUnit with Fluent Assertions for a friendlier, human-readable assert style):

[Fact]
public Task primitive_types_should_not_default_to_nothing()
{
    var input = new Something<int>(0);
    var output = input.Bind(x => x + 10);
    (output as Something<int>).Value.Should().Be(10);
}

This test stores an integer with the value of 0 in a Maybe and then attempts to Bind() it into a value 10 higher—i.e., it should equal 10. In the existing code, the switch operation inside Bind() would consider the value 0 to be default and would switch the return type from Something<int> to Nothing<int>. The function to add 10 wouldn’t be carried out, meaning that output in the unit test would be switched into a null, and the test would fail with a null reference exception.

Arguably, though, this isn’t correct behavior, as 0 is a valid value of int. The code is easily fixed with an additional line in the Bind() function, however:

public static Maybe<TOut> Bind<TIn, TOut>(
    this Maybe<TIn> @this,
    Func<TIn, TOut> f)
{
    try
    {
        Maybe<TOut> updatedValue = @this switch
        {
            Something<TIn> s when
                !EqualityComparer<TIn>.Default.Equals(s.Value, default) =>
                    new Something<TOut>(f(s.Value)),
                // This is the new line
            Something<TIn> s when
            s.GetType().GetGenericArguments()[0].IsPrimitive =>
                new Something<TOut>(f(s.Value)),
            Something<TIn> _ => new Nothing<TOut>(),
            Nothing<TIn> _ => new Nothing<TOut>(),
            Error<TIn> e => new Error<TOut>(e.ErrorMessage),
            _ => new Error<TOut>(
                new Exception("New Maybe state that isn't coded for!: " +
                    @this.GetType()))
        };
        return updatedValue;
    }
    catch (Exception e)
    {
        return new Error<TOut>(e);
    }
}

The new line checks the first generic argument of the Maybe<T>—i.e., the “real” type of T. All the types I listed at the beginning of this section would have a value of IsPrimitive set to true.

If we were to rerun the unit test with this modified Bind() function, the 0-valued int still wouldn’t match on the check against not being default, but the next line would match, because int is a primitive.

This does now mean that all primitives are incapable of being a Nothing<T>. Whether that’s right or wrong is a matter for you to assess. You might want to consider it a Nothing<T> if T is a bool, for example. If that’s the case, another case would need to be added to the switch between the first two lines to handle the specific case of T being bool.

It might also be important to a calculation that it be possible for a Boolean false to be passed into a function to perform a calculation. As I said, it’s a question you can best answer yourself.

One way to avoid this situation altogether is to always pass a nullable class around as T so you can be sure that you’re getting the correct behavior when trying to decide whether what you’re looking at is Something or Nothing.

Maybe and Logging

Another thing worth considering for the use of monads in a professional environment is the all-important developer’s tool: logging. Logging information—not just errors, but also all sorts of important information—about the status of a function’s progress is often crucial. Not just errors, but also all sorts of important information.

It’s possible to do something like this, of course:

var idMaybe = idValue.ToMaybe();
var transOne = idMaybe.Bind(x => transformationOne(x));
if(transOne is Something<MyClass> s)
{
    this.Logger.LogInformation("Processing item " + s.Value.Id);
}
else if (transOne is Nothing<MyClass>)
{
    this.Logger.LogWarning("No record found for " + idValue");
}
else if (transOne is Error<MyClass> e)
{
    this.Logger.LogError(e, "An error occurred for " + idValue);
}

But this approach is likely to balloon out of hand if we do much of it—especially if there are many binds in the process that all require logging.

We might be able to leave out the error log until the very end, or even all the way out in the controller or whatever else ultimately originated this request. The error message would be passed from hand to hand untouched. But that still leaves occasional log messages for information or warning purposes.

I prefer to add extension methods to the Maybe to provide a set of event handler functions:

public static class MaybeLoggingExtensions
{
    public static Maybe<T> OnSomething(this Maybe<T> @this, Action<T> a)
    {
        if(@this is Something<T>)
        {
            a(@this);
        }

     return @this;
    }

    public static Maybe<T> OnNothing(this Maybe<T> @this, Action a)
    {
        if(@this is Nothing<T> _)
        {
            a();
        }

        return @this;

    public static Maybe<T> OnError(this Maybe<T> @this, Action<Exception> a)
    {
        if(@this is Error<T> e)
        {
            a(e.CapturedError);
        }

        return @this;
    }
}

The way I’d use this then, is more like this:

var idMaybe  idValue.ToMaybe();
var transOne = idMaybe.Bind(x => transformationOne(x))
    .OnSomething(x => this.Logger.LogInformation("Processing item " + x.Id))
    .OnNothing(() => this.Logger.LogWarning("No record found for " + idValue))
    .OnError(e => this.Logger.LogError(e, "An error occurred for " + idValue));

This is fairly usable, although it does have a drawback. The OnNothing() and OnError() states will proliferate from Bind() to Bind() unmodified, so if we have a long list of Bind() calls with OnNothing() or OnError() handler functions, they’ll all fire every time, like this:

var idMaybe  idValue.ToMaybe();
var transOne = idMaybe.Bind(x => transformationOne(x))
    .OnNothing(() => this.Logger.LogWarning("Nothing happened one");
var transTwo = transOne.Bind(x => transformationTwo(x))
    .OnNothing(() => this.Logger.LogWarning("Nothing happened two");
var returnValue = transTwo.Bind(x => transformationThree(x))
    .OnNothing(() => this.Logger.LogWarning("Nothing happened three");

In this code sample, all three OnNothing() functions will fire, and three warning logs will be written. You may want that or you may not. It might not be all that interesting after the first Nothing.

I do have a solution for this issue, but it means quite a lot more coding. Create a new instance of Nothing and Error that descend from the originals:

public class UnhandledNothing<T> : Nothing<T>
{
}

public class UnhandledError<T> : Error<T>
{
}

We’d also need to modify the Bind() function so that these are the types that are returned when switching from the Something path to one of these:

public static Maybe<TOut> Bind<TIn, TOut>(
    this Maybe<TIn> @this,
    Func<TIn, TOut> f)
{
    try
    {
        Maybe<TOut> updatedValue = @this switch
        {
            Something<TIn> s when
                !EqualityComparer<TIn>.Default.Equals(s.Value, default) =>
                    new Something<TOut>(f(s.Value)),
            Something<TIn> s when
                s.GetType().GetGenericArguments()[0].IsPrimitive =>
                    new Something<TOut>(f(s.Value)),
            Something<TIn> _ => new UnhandledNothing<TOut>(),
            Nothing<TIn> _ => new Nothing<TOut>(),
            UnhandledNothing<TIn> _ => new UnhandledNothing<TOut>(),
            Error<TIn> e => new Error<TOut>(e.ErrorMessage),
            UnhandledError<TIn> e => new UnhandledError<TOut>(e.CapturedError),
            _ => new Error<TOut>(
                new Exception("New Maybe state that isn't coded for!: " +
                    @this.GetType()))
        };
        return updatedValue;
    }
    catch (Exception e)
    {
        return new UnhandledError<TOut>(e);
    }
}

Then, finally, we need to update the handler functions:

public static class MaybeLoggingExtensions
{

 public static Maybe<T> OnNothing(this Maybe<T> @this, Action a)
    {
        if(@this is UnhandledNothing<T> _)
        {
            a();
            return new Nothing<T>();
        }

        return @this;
    }

    public static Maybe<T> OnError(this Maybe<T> @this, Action<Exception> a)
    {
        if(@this is UnhandledError<T> e)
        {
            a(e.CapturedError);
            return new Error<T>(e.CapturedError);
        }

        return @this;
    }
}

When a switch happens from Something to one of the other states, the Maybe switches to a state that not only signals that either Nothing or an Exception occurred, but also that nothing has yet handled that state.

Once one of the handler functions is called, and an unhandled state is found, then the callback is triggered to log (or whatever) and a new object is returned that maintains the same state type, but this time indicating that is no longer unhandled.

In the previous example with multiple Bind() calls with OnNothing() functions attached, only the first OnNothing() will actually be triggered; the rest will be ignored.

Nothing is stopping you from still using a pattern-matching statement to examine the type of the Maybe to perform an action after the Maybe reaches its final destination, elsewhere in the codebase.

Maybe and Async

I know what you’re going to ask me next. Look, I’m going to have to let you down gently. I’m already married. Oh? My mistake. Async and monads. Yeah, OK. Moving on…​

How do you handle calls inside a monad to processes that are asynchronous? Honestly, it’s not all that hard. Leave the Maybe Bind() functions we’ve already written and add these to the codebase as well:

public static async Task<Maybe<TOut>> BindAsync<TIn, TOut>(
     this Maybe<TIn> @this,
     Func<TIn, Task<TOut>> f)
{
    try
    {
        Maybe<TOut> updatedValue = @this switch
        {
            Something<TIn> s when
                EqualityComparer<TIn>.Default.Equals(s.Value, default) =>
                    new Something<TOut>(await f(s.Value)),
            Something<TIn> _ => new Nothing<TOut>(),
            Nothing<TIn> _ => new Nothing<TOut>(),
            Error<TIn> e => new Error<TOut>(e.ErrorMessage),
            _ => new Error<TOut>(
                new Exception("New Maybe state that isn't coded for!: " +
                    @this.GetType()))
        };
        return updatedValue;
    }
    catch (Exception e)
    {
        return new Error<TOut>(e);
    }
}

All we’ve done here is wrap another layer around the value we’re passing around. The first layer is the Maybe—representing that the operation we’re trying may not have worked. The second layer is the Task—representing that an async operation needs to be carried out first, before we can get to the Maybe.

Using this out in your wider codebase is probably best done a line at a time so you can avoid mixing up the async and non-async versions in the same chain of Bind() calls. Otherwise, you could end up with a Task<T> being passed around as a type, rather than the actual type, T. Also, it means you can separate out each async call and use an await statement to get the real value to pass along to the next Bind() operation.

Nested Maybes

A problem scenario can occur with the Maybes I’ve shown so far. I came to realize this scenario existed only after I’d changed an awful lot my interfaces to have Maybe<T> as the return type for anything involving an external interaction.

Imagine that we’ve created a couple of data loaders of some description. They could be databases, web APIs, or something else; it doesn’t matter:

public interface DataLoaderOne
{
    Maybe<string> GetStringOne();
}

public interface DataLoaderTwo
{
    Maybe<string> GetStringTwo(string stringOne);
}

public interface DataLoaderThree
{
    Maybe<string> GetStringThree(string stringTwo);
}

In some other part of the code, we want to orchestrate calls to each of these interfaces in turn by using a Bind() call.

Note that the point of the Maybe<string> return type is that we can reference them through Bind() functions, and if any of the dataLoader calls fail, the subsequent steps won’t be executed and we’ll get a Nothing<string or Error<string> at the end to examine:

    var finalString = dataLoaderOne.GetStringOne()
        .Bind(x => dataLoaderTwo.GetStringTwo(x))
        .Bind(x => dataLoaderThree.GetStringThree(x));

What we’ll find, though, is that this code won’t compile. Why do you suppose that is?

Three function calls are at work here, and all three return the type Maybe<string>. Look at what happens a line at a time:

  1. GetStringOne() returns a Maybe<string>. So far, so good.

  2. The Bind() call attaches to the Maybe<string> and unpacks it to a string to pass to GetStringTwo(), whose return type is popped into a new Maybe for safekeeping.

  3. The next Bind() call unwraps the return type of that last bind so that it’s only the return type of GetStringTwo()—but GetStringTwo() didn’t return a string; it returned Maybe<string>. So, on this second Bind() call, x is actually equal to Maybe<string>, which can’t be passed into GetStringThree()!

We could solve this by directly accessing the value out of the Maybe stored in x, but first we’d need to cast it to a Something. What if it weren’t a something, though? What if an error had occurred in GetStringOne() talking to the database? What if no string could be found?

We basically need a way to unpack a nested Maybe, but only in the event that it returns a Something containing a real value. In all other cases, we need to match its unhappy path (Nothing or Error).

One way to do it is with another Bind() function to sit alongside the other two already created, but this one specifically handles the issue of nested Maybes.

We could do it like this:

public static Maybe<TOut> Bind<TIn, TOut>(
    this Maybe<Maybe<TIn>> @this, Func<TIn, TOut> f)
{
    try
    {
        var returnValue = @this switch
        {
            Something<Maybe<TIn>> s => s.Value.Bind(f),
            Error<Maybe<TIn>> e => new Error<TOut>(e.ErrorMessage),
            Nothing<Maybe<TIn>> => new Nothing<TOut>(),
            _ => new Error<TOut>(
                new Exception(
                    "New Maybe state that isn't coded for!: " + @this.GetType()))

        };
        return returnValue;


    }
    catch (Exception e)
    {
        return new Error<TOut>(e);
    }
}

We’re taking the nested bind (Maybe<Maybe<string>>) and calling Bind() on it, which unwraps the first layer, leaving simply Maybe<string> inside the Bind() callback function. From there, we can just use the exact same logic on the Maybe as in previous Bind() functions.

We would need do this for the async version as well:

public static async Task<Maybe<TOut>> BindAsync<TIn, TOut>(
    this Maybe<Maybe<TIn>> @this,
    Func<TIn, TOut> f)
{
    try
    {
        var returnValue = await @this.Bind(async x =>
       {
             var updatedValue = @this switch
             {
              Something<TIn> s when
               EqualityComparer<TIn>.Default.Equals(s.Value, default(TIn))  =>
                new Something<TOut>(await f(s.Value)),
              Something<TIn> _ => new Nothing<TOut>(),
              Nothing<TIn> _ => new Nothing<TOut>(),
              Error<TIn> e => new Error<TOut>(e.CapturedError)
             }
             return updatedValue;
       });
       return returnValue;
    }
    catch(Exception e)
    {
        return new Error<TOut>(e);
    }
}

If you want another way to think about this process, think of the SelectMany() function in LINQ. If we feed it an array of arrays—a multidimensional array—we get back a single-dimension, flat array. This handling of nested Maybe objects now allows us to do the same thing with monads. This is actually one of the “laws” of monads—the properties that anything that calls itself a monad is expected to follow.

In fact, that leads me neatly onto the next topic. What exactly are laws, what are they for, and how do we make sure our C# monads follow them too?

The Laws

Strictly speaking, a true monad must conform to a set of rules (known as laws). I’ll briefly talk through each of these laws so you’ll know for yourself whether you’re looking at a real monad.

Left Identity Law

The left identity law states that a monad, given a function as a parameter to its Bind() method, will return something that is entirely the equivalent of just running the function directly with no side effects. Here’s some C# code to demonstrate:

Func<int, int> MultiplyByTwo = x => x * 2;
var input = 100;
var runFunctionOutput = MultiplyByTwo(input);

var monadOutput = new Something<int>(input).Bind(MultiplyByTwo);

// runFunctionOutput and monadOutput.Value should both
// be identical - 200 - to conform to the Left Identity Law.

Right Identity Law

Before I explain the right identity law, I need to move back a few steps. First, I need to explain functors. These are functions that convert a thing, or list of things, from one form into another. Map(), Bind(), and Select() are all examples of functors.

The simplest functor that exists is the Identity functor. This is a function that, given an input, returns it unaltered and with no side effects. The Identity functor can have its uses, when you’re composing functions together.

The only reason to be interested in it here and now is that it’s the basis of the second monad law: the right identity law. This law indicates that a monad, when given an Identity functor in its Bind() function, will return the original value with no side effects.

We could test the Maybe created in Chapter 6 like this:

Func<int,int> identityInt = (int x) => x;
var input = 200;
var result = new Something<int>(input).Bind(identityInt);
// result = 200

The Maybe takes a function that doesn’t result in an error or null, executes it, and then returns exactly whatever comes out of it and nothing else.

The basic gist of both of these first two laws is simply that the monad can’t interfere in any way with the data coming in or going out, or in the execution of the function provided as a parameter to the Bind() method. A monad is simply a pipe down which functions and data flow.

Associativity Law

The first two laws should be fairly trivial, and our Maybe implementation fulfills them both. The last law, the associativity law, is a bit harder to explain.

It basically means that the way the monads are nested doesn’t matter; you always end up with a single monad containing a value at the end. Here’s a simple C# example:

var input = 100;
var op1 = (int x) => x * 2;
var op2 = (int x) => x + 100;

var versionOne = new Something<int>(input)
    .Bind(op1)
    .Bind(op2);

    // versionOne.Value = 100 * 2 + 100 = 300

    var versionTwo = new Something<int>(input)
        .Bind(x => new Something<int>(x).Bind(op1)).Bind(op2);

     // If we don't implement something to fulfill the
     // associativity law, we'll end up with a type of
     // Something<Something<int>>, where we want this to be
     // the exact same type and value as versionOne

Look back to my description of how to deal with nested Maybes in “Nested Maybes” and you’ll see how this code is implemented.

With any luck, now that you’ve seen the three monad laws, I’ve proven that my Maybe is a proper, no-holds-barred, honest-to-dog monad.

In the next section, I’ll show you another monad you might use to do away with the need for storing variables that need to be shared.

Reader

Let’s imagine for a moment that we’re putting together a report of some kind. It does a series of pulls of data from an SQL Server database.

First, we need to grab the record for a given user. Next, using that record, we get their most recent order from our entirely imaginary bookshop.8 Finally, we turn the most recent Order record into a list of items from the order and return a few details from them in a report.

We want to take advantage of a monad-style Bind() operation, so how do we ensure that the data from each step is passed along with the database connection object? That’s no problem; we can throw together a tuple and simply pass along both objects:

public string MakeOrderReport(string userName) =>
    (
        Conn: this.connFactory.MakeDbConnection(),
        userid
    )
    .Bind(x => (
        x.Conn,
        Customer: this.customerRepo.GetCustomer(x.Conn, x.userName)
    )
    .Bind(x => (
        x.Conn,
        Order: this.orderRepo.GetCustomerOrders(x.Conn, x.Customer.Id)
    ),
    .Bind(x => this.Order.Items.First())
    .Bind(x => string.Join("
", x));

This is a workable solution, but it’s a bit ugly. A few repeated steps exist only to allow the connection object to be persisted between Bind() operations. It’s harming the readability of the function.

The function isn’t pure either, if you think about it. It has to create a database connection, which is a form of side effect.

We can use another functional structure to solve all these problems: the Reader monad. It’s FP’s answer to dependency injection, but on the functional level, rather than into a class.

In the case of the preceding function, it’s the IDbConnection that we want to inject so it can be instantiated elsewhere, leaving the MakeOrderReport() pure—i.e., free of any side effects.

Here’s an incredibly simple use of Reader:

var reader = new Reader<int, string>(e => e.ToString());
var result = reader.Run(100);

This code defines a Reader, which takes a function that it stores but doesn’t execute. The function has an “environment” variable type as its parameter—this is the currently unknown value we’re going to inject in the future—and returns a value based on that parameter (in this case, an integer).

The “int → string” function is stored in Reader on the first line. On the second line, we call the Run() function, which provides the missing environment variable value (here, 100). Since the environment has finally been provided, Reader can therefore use it to return a real value.

Since this is a monad, that also means we should have Bind() functions to provide a flow. This is how they’d be used:

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

var result = reader.Run(100);

Note that the type of the reader variable is Reader<int, string>. That’s because every Bind() call places a wrapper around the previous function that has the same alternate return type but a different parameter. In the first line with the parameter e => e * 100, that function will be executed later, after Run(). And so on…​

This is a more realistic use of Reader:

public Customer GetCustomerData(string userName, IDbConnection db) =>
    new Reader(this.customerRepo.GetCustomer(userName, x))
    .Run(db);

Alternatively, we can simply return Reader and then allow the outside world to continue using Bind() calls to modify it further before the Run() function turns it into a proper value:

public Reader<IdbConnection, User> GetCustomerData(string userName) =>
    new Reader(this.customerRepo.GetCustomer(userName, x));

This way, the same function can be called many times, with the Reader’s Bind() function used to convert it to the actual type we want. For example, say we want to get the customer’s order data:

var dbConn = this.DbConnectionFactory.GetConnection();

var orders = this._customerRepo.GetCustomerData("simon.painter")
    .Bind(X => x.OrderData.ToArray())
    .Run(dbConn);

Think of this approach as creating a box that can be opened only by inserting a variable of the correct type.

The use of Reader also means it’s easy to inject a mock IDbConnection into these functions and write unit tests based on them.

Depending on how we want to structure our code, we could even consider exposing Readers on interfaces. We don’t have to pass in a dependency like a DbConnection; we could pass in an ID value for a database table or anything we like—something like this, perhaps:

public interface IDataStore
{
    Reader<int,Customer> GetCustomerData();
    Reader<Guid,Product> GetProductData();
    Reader<int,IEnumerable<Order>> GetCustomerOrders();
}

You could use this in all sorts of ways; the choice is all a matter of what suits you and what you’re trying to do. In the next section, I’ll show a variation on this idea—the State monad.

State

In principle, a State monad is similar to Reader. A container is defined that requires some form of state object to convert itself into a proper final piece of data. Bind() functions are used to provide additional data transformations, but nothing will happen until State is provided.

State differs from Reader in two ways:

  • Instead of an environment type, it’s known as the state type.

  • Two items, not one, are being passed between Bind() operations.

In a Reader monad, the original environment type is seen only at the beginning of the chain of Bind() functions. With a State monad, the type persists all the way through to the end. The State type, and whatever the current value is set to, are stored in a tuple that is passed from one step to the next. Both the value and State can be replaced with new values each time. The value can change types, but State is a single type throughout the whole process that can have its values updated, if required.

You can also arbitrarily fetch or replace the State object in the State monad at any time by using functions.

My implementation doesn’t strictly adhere to the way you’ll see it in languages like Haskell, but I’d argue that implementations of that kind are a pain in C#, and I’m not convinced that there’s any point to doing it. The version I’m showing you here could well have some use in daily C# coding:

public class State<TS, TV>
{
    public TS CurrentState { get; init; }
    public TV CurrentValue { get; init; }
    public State(TS s, TV v)
    {
        CurrentValue = v;
        CurrentState = s;
    }
}

The State monad doesn’t have multiple states, so there’s no need for a base abstract class. It’s just a simple class with two properties—a value and a state (i.e., the thing we’ll pass along through every instance).

The logic has to be implemented in extension methods:

public static class StateMonadExtensions
{

public static State<TS, TV> ToState<TS, TV>(this TS @this, TV value) =>
    new(@this, value);

public static State<TS, TV> Update<TS, TV>(
        this State<TS, TV> @this,
        Func<TS, TS> f
    ) => new(f(@this.CurrentState), @this.CurrentValue);
}

As usual, we don’t have a lot of code to implement but plenty of interesting effects.

This is the way I’d use it:

public IEnumerable<Order> MakeOrderReport(string userName) =>
    this.connFactory.MakeDbConnection().ToState(userName)
        .Bind((s, x) => this.customerRepo.GetCustomer(s, x))
        .Bind((s, x) => this.orderRepo.GetCustomreOrders(s, x.Id))

The idea here is that the state object is being passed along the chain as s, and the result of the last Bind() is passed as x. Based on both of those values, we can determine what the next value should be.

This just leaves out the ability to update the current state. We’d do that with this extension method:

public static State<TS, TV>Update<TS,TV>(
    this State<TS,TV> @this,
    Func<TS, TS> f
) => new(@this.CurrentState, f(@this.CurrentState));

Here’s a simple example for the sake of illustration:

var result = 10.ToState(10)
    .Bind((s, x) => s * x)
    .Bind((s, x) => x - s) // s=10, x = 90
    .Update(s => s - 5) // s=5, x = 90
    .Bind((s, x) => x / 5); // s=5, x = 18

Using this technique, we can have arrow functions with a few bits of state that will flow from one Bind() operation to the next, and that we can even update when needed. The technique prevents us from either being forced to turn our neat arrow function into a full function with curly braces, or passing a large, ungainly tuple containing the read-only data through each Bind().

This implementation has left off the form you’ll find in Haskell, where the initial State value is passed in only when the complete chain of Bind() functions has been defined, but I’d argue that in a C# context, this version is more useful and certainly an awful lot easier to code!

Maybe a State?

You may notice in the previous code sample that there is no way to use the Bind() function like a Maybe, to capture error conditions and null results coming back in any of the several possible states. Is it possible to merge Maybe and Reader into a single monad that both persists a State object and handles errors?

Yes, and we could accomplish this in several ways, depending on how exactly we’re planning to use the monad. I’ll show you my preferred solution. First, we adjust the State class so that instead of a value, it stores a Maybe containing the value:

public class State<TS, TV>
{
    public TS CurrentState { get; init; }
    public Maybe<TV> CurrentValue { get; init; }
    public State(TS s, TV v)
    {
        CurrentValue = new Something<TV>(v);
        CurrentState = s;
    }
}
}

Then we adjust the Bind() function to take Maybe into account, but without changing the signature of the function:

public static State<TS, TNew> Bind<TS, TOld, TNew>(
    this State<TS, TOld> @this, Func<TS, TOld, TNew> f) =>
    new(@this.CurrentState, @this.CurrentValue.Bind(
        x => f(@this.CurrentState, x))
    );

The usage is just about exactly the same, except that Value is now of type Maybe<T> instead of simply T. That affects only the return value from the container function, though:

public Maybe<IEnumerable<order>> MakeOrderReport(string userName) =>
    this.connFactory.MakeDbConnection().ToState(userName)
        .Bind((s, x) => this.customerRepo.GetCustomer(s, x)
        .Bind((s, x) => this.orderRepo.GetCustomerOrders(s, x.Id))

Whether you want to merge the concepts of the Maybe and the State monads in this way or would rather keep them separate is entirely up to you. If you do follow this approach, you’d just need to make sure to use a switch expression to translate Maybe into a single, concrete value at some point.

Here’s a last point to bear in mind too: the CurrentValue object of the State monad doesn’t have to be data; it can be a Func delegate, allowing you to have a bit of functionality ported between Bind() calls.

In the next section, I’ll show you what else might be a monad you’ve already been using in C#.

Examples You’re Already Using

Believe it or not, you’ve likely already been using monads for a while if you’ve been working with C# for any amount of time. Let’s take a look at a few examples.

Enumerable

If an enumerable isn’t a monad, it’s as close as it gets, at least after we invoke LINQ, which as you already know is developed based on FP concepts.

The enumerable Select() method operates on individual elements within an enumerable, but it still obeys the left identity law:

var op = x => x * 2;
var input = new [] { 100 };

var enumerableResult = input.Select(op);
var directResult = new [] { op(input.First()) };
// both equal the same value - { 200 }

The enumerable also obeys the right identity law:

var op = x => x;
var input = new [] { 100 };

var enumerableResult = input.Select(op);
var directResult = new [] { op(input.First()) };
// both equal the same value - { 100 }

That just leaves the associativity law, which is still a necessity to be considered a true monad. Does the enumerable follow that? It does, of course. By use of SelectMany().

Consider this:

var createEnumerable = (int x) => Enumerable.Range(0, x);
var input = new [] { 100, 200 }
var output = input.SelectMany(createEnumerable);
// output = single dimension array with 300 elements

There we have it, nested enumerables outputted as a single enumerable. That’s the associativity law. Ipso facto, QED, and so on. Yeah. Enumerables are monads.

Task

What about tasks? Are they monads too? I bet you a beer of your choice that they’re absolutely monads, and I can prove it.9 Let’s run through the laws once again.

According to the left identity law, a function call with a task should match calling the function call directly. That’s slightly tricky to prove, in that an async method always returns a type of Task or Task<T>, which is the same as Maybe<T> in many ways, if you think about it. It’s a wrapper around a type that might or might not resolve to actual data. But if we move back a level of abstraction, we can still demonstrate that the law is obeyed:

public Func<int> op = x => x * 2;
public async Task<int> asyncOp(int x) => await Task.FromResult(op(x));

var taskResult = await asyncOp(100);
var nonTaskResult = op(100);
// The result is the same - 200

I’m not saying that this is C# code I’d necessarily be proud of, but it does at least prove the point that whether we call op through an async wrapper method or not, the result is the same. That’s the left identity law confirmed. How about the right identity law? Honestly, that’s roughly the same code again:

// Notice the function is simply returning x back again unchanged this time.
public Func<int> op = x => x;
public async Task<int> asyncOp(int x) => await Task.FromResult(op(x));

var taskResult = await asyncOp(100);
var nonTaskResult = op(100);
// The result is the same as the initial input - 100

That’s the identity laws settled. What about the equally important associativity law? Believe it or not, there is a way to demonstrate this with tasks:

async Task<int> op1(int x) => await Task.FromResult(10 * x);
async Task<int> pp2() => await Task.FromResult(100);
var result = await op1(await pp2());
// result = 1,000

Here we have a Task<int> being passed into another Task<int> as a parameter, but with nested calls to await, it’s all possible to flatten out to a simple int, which is the actual type of result.

Hopefully, I’ve earned my beer. Mine’s a pint, please.10 A European-style half-liter is fine as well.

Other Structures

Honestly and truly—if you’re OK with my version of the Maybe monad and aren’t bothered about going further, feel free to skip ahead to Chapter 8. You can easily achieve most of what you likely want with Maybe alone. I’m going to describe a few other kinds of monads that exist out there in the wider FP language world, which you might want to consider implementing in C#.

These monads might be of interest if squeezing out the last few vestiges of non-functional code from C# is your intention. They might also be of interest from a theoretical perspective. It’s entirely up to you, though, whether to take the monad concept further and continue to implement these others.

Now, strictly speaking, the version of the Maybe monad I’ve been building up over this chapter and Chapter 6 is a mix of two monads. A true Maybe monad has only two states: Something (or Just) and Nothing (or Empty). That’s it.

The monad for handling error states is the Either (aka Result) monad. That has two states: Left and Right. Right is the “happy” path, where every function passed into the Bind() command works, and all is right with the world. Left is the “unhappy” path, where an error of some kind occurs, and the error is contained in Left.

The Left and Right naming convention presumably comes from the recurring concept in many cultures that the left hand is evil and the right hand is good. This is even captured in bits of our language—the Latin word for “left” is “sinister.” In these enlightened days, however, we no longer drive out left-handed folks from our homes or whatever it is they used to do.11

I won’t spell out that implementation here; you can basically achieve it by taking my version of Maybe and removing the Nothing class.

Similarly, you can make a true Maybe by removing the Error class—although I’d argue that by combining the two into a single entity, you have something that can handle just about any situation you’re likely to encounter when interacting with external resources.

Is my approach pure and fully correct classical functional theory? No. Is it useful in production code? 100% yes.

Many more monads exist beyond Maybe and Either, and if you move into a programming language like Haskell, you’ll likely make regular use of them. Here are a few examples:

Identity

A monad that simply returns whatever value you feed in. This monad can be useful when getting into deeper functional theory in a more purely functional language, but doesn’t really have an application here in C#.

IO

Used to allow interactions with external resources without introducing impure functions. In C#, we can follow the inversion-of-control pattern (i.e., dependency injection) to allow us to get around any issues for testing, etc.

Writer

Used to allow something like a logfile entry to be generated with each Bind() operation that’s passed to the monad. I’m not sure there’s any special benefit to implementing this in C#, however.

As you can see from this list, many other monads are available in the FP world, but I’d argue that most or all don’t provide any real benefit to us. At the end of the day, C# is a hybrid functional/object-oriented language. It has been extended to allow support for FP concepts, but it’s never going to be a purely functional language, and there’s no benefit to trying to treat it as such.

I strongly recommend experimenting with the Maybe/Either monad, but beyond that, I honestly wouldn’t bother, unless you’re curious to see just how far you can push the idea of FP in C#.12 It’s not necessarily something for your production environment, though.

In the final section, I’ll provide a complete worked example of how to use monads in an application.

A Worked Example

OK, here we go. Let’s put it all together in one great, epic heap of monady-functional goodness. We’ve already talked holidays in this chapter’s examples, so this time I’m going to focus on how we’re going to get to the airport. This will need a series of lookups and data transformations, all of which would normally require error handling and branching logic if we were to follow a more conventional, object-oriented approach. I hope you’ll agree that by using FP techniques and monads, the code looks quite considerably more elegant.

First off, we need our interfaces. We’re not going to code each and every single dependency of the code here, so let’s just define the interfaces we’ll need:

public interface IMappingSystem
{
    Maybe<Address> GetAddress(Location l);
}

public interface IRoutePlanner
{
    Task<Maybe<Route>> DetermineRoute(Address a, Address b);
}

public interface ITrafficMonitor
{
    Maybe<TrafficAdvice> GetAdvice(Route r);
}

public interface IPricingCalculator
{
    decimal PriceRoute(Route r);
}

Now we’ll write the code to consume them all. In the specific scenario I’m imagining, it’s the near future. Driverless cars have become a thing. Most people no longer own personal vehicles and simply use an app on their phones to have a car brought out of the cloud of automated vehicles, direct to their homes.

Here’s the process:

  1. The initial inputs are the starting location and destination, provided by the user.

  2. Each of these locations needs to be looked up in a mapping system and converted to proper addresses.

  3. The user’s account needs to be fetched from the internal data store.

  4. The route needs to be checked with a traffic service.

  5. A pricing service has to be called to determine a charge for the journey.

  6. The price is returned to the user.

In code, that process might look like this:

public Maybe<decimal> DeterminePrice(Location from, Location to)
{
    var addresses = this.mapping.GetAddress(from).Bind(x =>
        (From: x,
         To: this.mapping.GetAddress(to)));

    var route = await addresses.BindAsync (async x =>
        await this.router.DetermineRoute(x.From, x.To));

    var trafficInfo = route.Bind(x => this.trafficAdvisor.GetAdvice(x));
    var hasRoadWorks = trafficInfo is Something<TrafficAdvice> s &&
        s.Value.RoadworksOnRoute;

    var price = route.Bind(x => this.pricing.PriceRoute(x));
    var finalPrice = route.Bind(x => hasRoadWorks ? x *= 1.1 : x);

    return finalPrice;
}

This is far easier, isn’t it? Before I end this chapter, I want to unpack a few details of what’s happening in this code sample.

First, there’s no error handling anywhere. Any of those external dependencies could result in an error being thrown, or no details being located in their respective data stores. The monad Bind() function handles all that logic. If, for example, the router is unable to determine a route (maybe a network error occurs), Maybe will be set to an Error<Route> at that point, and none of the subsequent operations will be executed. The final return type will be Error<decimal>, because the Error class is re-created at each step, but the actual Exception is passed between instances. The outside world is responsible for doing something with the final returned value once it receives that value and checks to see whether it’s a Something<decimal> or a form of error state. The receiving code can then decide how to communicate this information to the end user.

If we followed the OOP approach to this code, the function would most likely be two to three times longer, in order to include try/catch blocks and checks against each object to confirm that they’re valid.

I’ve used tuples when I want to build up a set of inputs. In the case of the Address objects, this means that if the first address isn’t found, the lookup for the second won’t be attempted. It also means that both of the inputs required by the second function to be run are available in a single location, which we can then access with another Bind() call (assuming a real value is returned by the address lookup).

The final few steps don’t actually involve calls to external dependencies, but by continuing to use the Bind() function, anything inside its parameter lambda expression can be written with the assumption that a real value is available, because if there weren’t, the lambda wouldn’t be executed.

And there we have it, a pretty much fully functional bit of C# code. I hope it is to your liking.

Summary

In this chapter, we explored the dreaded FP concept that has been known to make grown developers tremble in their inexpensive footwear. All being well, monads shouldn’t be a mystery to you any longer.

I’ve shown you how to do the following:

  • Massively reduce the amount of required code

  • Introduce an implicit error-handling system

You’ve accomplished this by using the Maybe monad, along with learning how to make one yourself.

In the next chapter, I’ll briefly present the concept of currying. I’ll see you on the next page.

1 The first few examples of a Bind() function presented in this chapter are modeled after those used by Enrico Buonanno in his book Functional Programming in C# (Manning). The rest are my own derivatives of his work.

2 I would say non-null, but integers default to 0 and Booleans to false.

3 Or the living Schrödinger’s cat. Actually, is that the safe option? I’ve owned cats; I know what they’re like!

4 Cheesecake, preferably. Paul Hollywood would be very disappointed to learn I don’t like many cakes, but New York–style cheesecake is most certainly one that I like!

5 This library can be used for quickly and easily converting between types, if that’s something you do a lot in your code.

6 Not a kind of tea! Nor is it a porcine character from Dragonball.

7 Except bool, which defaults to false, and char, which defaults to ''.

8 I’m the imaginary owner, and you can be the imaginary person who helps customers find what they want. Aren’t I generous?

9 I like dark ales and European-style lagers. That strong stuff they make in Minnesota is terrific too.

10 That’s 568 milliliters. I’m aware other countries have other definitions of the word.

11 My brother is among the lefties. Hi, Mark—you’ve been mentioned in a programming book! I wonder whether you’ll ever know about it.

12 C# rather than .NET in general; F# is .NET as well.

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

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