One of the most surprising and useful things I’ve learned about .NET this year is that it has quite good support for monads as of .NET 3.5 (it’s just missing higher-order polymorphism). What’s more, for those allergic to monads, you don’t need to understand anything in that previous sentence to follow this post. :)
In this post we’ll implement the couple of functions necessary to be able to compose instances of
Lazy<T> together in interesting ways using LINQ and LINQ comprehensions.
As mentioned last post, I’ve been messing around with
Lazy<T> as a way of performing one-time, long-running retrieval of values in an asynchronous way. Some of these values depend upon each other, so I’d like a way to build a new
Lazy<T> from some existing lazy values, then hand them off to a background thread to populate. We’ll see an example of this at the end of the post.
Lazy LINQ extension methods
To play nicely with LINQ we need to implement a couple of extension methods for
SelectMany. Types for which these functions are implemented are monads1, which means we can combine them in interesting ways. In Haskell,
Select is known as
SelectMany is known as
>>= (pronounced “bind”).
Mapping inside with
Select implementation for
Lazy<T> works similarly to how it does for
IEnumerable<T>, it takes a
Func<T, TResult> and maps it over each value inside, and returns a new
Lazy<T>, it applies the selector function to the single value inside, returning a new
Mapping and flattening with
Now we come to
SelectMany. Rather than taking a
Func<T, TResult> like
Select, it works on
Func<T, Lazy<TResult>> instead.
The output of
selector(lazy.Value) is a
Lazy<TResult>. We don’t want to return a
Lazy<Lazy<TResult>>, so we need to flatten this out. We can do this by returning
selector(lazy.Value).Value, to get the inner
To work well with the
from .. select style of LINQ query we also want to implement another overload of
SelectMany that has an additional result combining function. We can also re-write our previous
SelectMany in terms of this new overload. Here’s the finished static class:
The last function is our new
SelectMany overload, which has the additional step of combining the results of both the first lazy value, and the second lazy value returned from the
It’s alive! It’s alive!
We can now test out our abomination. Here’s how
And here’s how
SelectMany works using LINQ comprehension syntax (we can also call the
SelectMany extension method directly if we like):
from .. select syntax gives us a nice way to express how we want to combine the values inside the
Lazy<T> instances. We call the value in the
x, and the string in the
str, then create a new lazy that combines
str + " cat " + x, all without ever really accessing the values until the
.Value property is called.
To illustrate that combining
Lazy<T> instances does not trigger evaluation, let’s write a test that increments a counter for each instance whenever evaluation occurs.
This is just like the deferred execution we get using LINQ for
IEnumerable<T>, where nothing is evaluated until we traverse the enumerable by using
foreach or a method like
Back to our motivating example
So back to our motivating example. We wanted to take several long-running operations, and calculate them once on a background thread. We’ll specify all these operations using
Lazy<T> instances, compose them together using our extension methods, and do the initialisation of the value using an async task (this is thread-safe by default for
Lazy<T> handles caching the calculation value, so subsequent accesses to any of the lazy instances involved in the calculation will now return the pre-calculated value immediately.
This test is just to show it working; for more realistic use we could try wrapping the task in an
IObservable<T> and calling subscribers back with the result. The first result will take some time, but subsequent calls will return the value back to the subscriber straight away.
So we’ve now seen how to combine
Lazy<T> instances in interesting ways using LINQ,
SelectMany. This let us control evaluation of several lazy instances by composing them and pushing initialisation into an async task.
More fundamentally, we’ve now got a bit of an idea about monads. A monad for a type is formed by a
SelectMany defined for the type, which enables the use of common composition patterns like
from .. select. Between this, standard LINQ enumerables, and using F#’s Option type in C# we can start to get an idea of how useful this pattern is.
A monad also needs a function which takes a value of
Tand returns a new instance of the monad with that value. This is called
returnin Haskell. We can use the
Lazy<T>constructor for this. There are also a few simple rules for how these functions should act and combine to make a valid monad instance, but we won’t go into them here.↩
This is a key part of how the monad interface works, so you’ll see this pattern crop up all the time when implementing