Computation Expressions in C# using async/await
Published: 2019-03-31 15:25:46

---
title: Computation Expressions in C# using async/await
published: true
tags: 
canonical_url: https://blog.neteril.org/blog/2018/01/11/computation-expressions-csharp-async-await/
---

I have had some fun already using C# 7.1 new customizable async/await pipeline to do interesting things:

- [ActivityTask, an helper for async/await on Android](/blog/2017/05/22/activitytask-async-await-android-cornercases/)
- [Recreating F# maybe computation expression in C#](/blog/2017/04/26/maybe-computation-expression-csharp/)

In this post I’ll be describing how I took on those past experiences and generalized the core idea of the second entry to provide a somewhat equivalent construct to F# computation expressions in C# by (ab)using the async/await machinery.

If you have never heard of F# computations expressions before I recommend reading the [F# for fun and profit serie](https://fsharpforfunandprofit.com/posts/computation-expressions-intro/) on it. Otherwise the TL;DR is that, without the monadic baggage, computation expressions essentially allow you to customize how a series of “statement” are chained together and plug your own logic in-between.

A famous example of such computation expression is the [async workflow](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/asynchronous-workflows) that lead to the creation of C# async/await in the first place (C# async/await being a more specialized/optimized form of its computation expression parent).

In a way what I’m describing in this post is basically how we can manage to return to the original definition of F# computation expressions using the more specialized C# async/await interface that was derived from it.

If you want to jump straight to the code [grab it on GitHub](https://github.com/garuma/Neteril.ComputationExpression). The repository also contains a [Workbook file](https://github.com/garuma/Neteril.ComputationExpression/blob/master/Examples.workbook) with a few examples utilizing the API so that you can get a feel of what it allows you to do.

For the others, here is the example given in the repository README of what the API looks like:

```
Option<int> TryDivide (int up, int down)
    => down == 0 ? None<int>.Value : Some.Of (up / down);

class MaybeBuilder : IMonadExpressionBuilder
{
    IMonad<T> IMonadExpressionBuilder.Bind<U, T> (IMonad<U> m, Func<U, IMonad<T>> f)
    {
        switch ((Option<U>)m) {
          case Some<U> some: return f (some.Item);
          case None<U> none:
          default: return None<T>.Value;
        }
    }

    public IMonad<T> Return<T> (T v) => Some.Of (v);
    public IMonad<T> Zero<T> () => None<T>.Value;
    // We don't have optional interface methods in C# quite yet
    public IMonad<T> Combine<T> (IMonad<T> m, IMonad<T> n) => throw new NotSupportedException ();
}

// Returns `Some 15`
ComputationExpression.Run<int, Option<int>> (new OptionExpressionBuilder (), async () => {
    var val1 = await TryDivide (120, 2);
    var val2 = await TryDivide (val1, 2);
    var val3 = await TryDivide (val2, 2);
    return val3;
})

// Returns `None`
ComputationExpression.Run<int, Option<int>> (new OptionExpressionBuilder (), async () => {
    var val1 = await TryDivide (120, 2);
    var val2 = await TryDivide (val1, 0);
    var val3 = await TryDivide (val2, 2);
    return val3;
})
```

In this snippet I’m lifting [the example of the Maybe/Option monad](/blog/2017/04/26/maybe-computation-expression-csharp/) I had talked about previously and encoding it via the computation expression mechanism to arrive at the same result. In that case the `await` keyword is used to represent the equivalent F# `let!` keyword which is a shortand for the `Bind` method of the computation expression builder (`Bind` is the core monadic operation that allows all those “statements” to be chained together).

To implement a custom computation expression you have to do two things:

1. Define a type that implements the empty `IMonad` interface. This simply acts as a marker to be able to use the custom async machinery of the library.
2. More interestingly, provide an implementation of the `IMonadExpressionBuilder` interface which is where you can put the code to customize the execution behavior of the computation expression block.

In the example above, the main customization is in the `Bind` method where we grab the result of executing the previous statement and then depending on the outcome either pass the result down the chain or short-circuit everything by returning `None`.

Another case where C# did a specialized implementation of what in F# is also encoded as a computation expression is `IEnumerable<T>` state machines via the `yield` operator.

With the library we can instead express this using the `Combine` method of the builder to achieve something like this (lifted from the examples workbook):

```
using static Neteril.ComputationExpression.ComputationExpression;
// The Enumerable monad is given in the library
using Neteril.ComputationExpression.Instances;

var result = ComputationExpression.Run<int, EnumerableMonad<int>> (new EnumerableExpressionBuilder (), async () => {
    var item = await (EnumerableMonad<int>)new [] { 1, 2, 3 };
    var item2 = await (EnumerableMonad<int>)new [] { 100, 200 };
    // We want back a enumeration containing the concatenation of (item, item2, item1 * item2)
    // for all successive values of item1 and item2
    await Yield (item);
    await Yield (item2);
    return item * item2;
});
string.Join (", ", result.Select (i => i.ToString ()));
// => [1, 100, 100, 1, 200, 200, 2, 100, 200, 2, 200, 400, 3, 100, 300, 3, 200, 600]
```

In this new sample, the monad is implemented in a way that the first `await` (again equivalent to F# `let!`) binds to each successive element of the given collections. The result is a `EnumerableMonad<T>` that is itself a `IEnumerable<T>` (i.e. you can further use LINQ on it).

Because in this case there is no way to hijack the C# `yield` operator directly (like F# would) this is instead represented here with the expression `await Yield` for intermediary values and `return` for the last one.

Here is the implementation of the builder for the sample to work (note the now implemented `Combine` method):

```
public class EnumerableExpressionBuilder : IMonadExpressionBuilder
{
    IMonad<T> IMonadExpressionBuilder.Bind<U, T> (IMonad<U> m, Func<U, IMonad<T>> f)
    {
       var previousEnumerableMonad = (EnumerableMonad<U>)m;
       return new EnumerableMonad<T> (previousEnumerableMonad.SelectMany (u => (EnumerableMonad<T>)f (u)));
    }

    IMonad<T> IMonadExpressionBuilder.Return<T> (T v) => new EnumerableMonad<T> (Enumerable.Repeat (v, 1));
    IMonad<T> IMonadExpressionBuilder.Zero<T> () => new EnumerableMonad<T> (Enumerable.Empty<T> ());

    IMonad<T> IMonadExpressionBuilder.Combine<T> (IMonad<T> m, IMonad<T> n)
    {
       var enumerableMonad1 = (EnumerableMonad<T>)m;
       var enumerableMonad2 = (EnumerableMonad<T>)n;
       // Simply use LINQ Concat method
       return new EnumerableMonad<T> (enumerableMonad1.Concat (enumerableMonad2));
    }
}
```

Interestingly of the early writings I remember reading about Monads and how it related to C#, the LINQ `SelectMany` operator was given as an example of a `Bind` operation in the monadic sense. With this we have the proof it’s indeed true.

The actual code enabling all of this is a fairly straightforward [async method builder implementation](https://github.com/garuma/Neteril.ComputationExpression/blob/master/Neteril.ComputationExpression/MonadAsyncMethodBuilder.cs). If you have read any of my previous articles I mentioned at the beginning of this post, it’s essentially doing the same thing with a bunch more hacks to cope with the added indirection of generic types in the `IMonad<T>` interface.

It also peeks a bit deeper into the internal structure of the async state machine generated by the compiler via the [`Machinist` class](https://github.com/garuma/Neteril.ComputationExpression/blob/master/Neteril.ComputationExpression/Machinist.cs) so that it can control the actual stepping and awaiter values that are stored in it. This for instance allows the same state machine to be “rewinded” which is essential to support cases like the above Enumerable sample.