Functional C# - Implementing Async Computations in C#

Update: Removed Internal Constructor constraint on AsyncBuilder.

As I covered earlier in my post Functional .NET - LINQ or Language Integrated Monads, I talked about using asynchronous computation expressions (monads) from C# 3.0.  Brian McNamara, of the F# team, posted back in May about using them from C#.  But since then, things have changed slightly.  Before, I showed a basic example of how to utilize the F# libraries from C#, but let's go deep under the covers to see how this actually works.

 

Getting Started

In order to make use of the F# libraries in our C# library, we need to add references to them.  We need the following items:

  • FSharp.Core.dll
  • FSharp.PowerPack.dll
  • FSharp.PowerPack.Linq.dll

And then I need to open the namespaces in order to take advantage of F#:

using Microsoft.FSharp;
using Microsoft.FSharp.Control;
using Microsoft.FSharp.Core;
 

Referencing F# Classes

As part of process of creating the asynchronous monad builders in C#, we need to create an instance of the F# class AsyncBuilder in the Microsoft.FSharp.Control namespace.  We can get a reference to this from the Pervasives class, through the async property.  This is a static reference which is available to all.

static class AsyncExtensions 

    public static AsyncBuilder async = Pervasives.async;
...

 

Now that we have this, we have to realize that F# doesn't use the standard .NET delegates for functions.  Let's walk through some ways of converting back and forth.

 

Converting Functions

As we've noted before, F# does not use the standard Func and Action delegates that are commonly used in C# and VB.NET.  Instead, the functions in F# use the FastFunc class.  This allows for the F# compiler to better optimize the closures, especially due to the fact that these closures are quite commonly used.  Another point of difference is that there is no distinction between Func and Action delegates, and instead, for functions that return no value have the return type of Unit.  This is the optimal way of handling this, due to the fact that Void is not treated as a real type, which you've heard me complain about in the past.

In order to convert from the Func delegate to the FastFunc, we use the FuncConvertExtensions class in the FSharp.PowerPack.Linq.dll assembly.  Then we can create extension methods on our Func delegates to expose the conversion methods.  The conversion methods should look like this.

static class AsyncExtensions
{
    public static FastFunc<Tuple<A, B>, C> ToTupledFastFunc<A, B, C>
        (this Func<A, B, C> f)
    {
        return FuncConvertExtensions.ToTupledFastFunc(f);
    }
    public static FastFunc<Tuple<A, B, C>, D> ToTupledFastFunc<A, B, C, D>
        (this Func<A, B, C, D> f)
    {
        return FuncConvertExtensions.ToTupledFastFunc(f);
    } 

    public static FastFunc<A, B> ToFastFunc<A, B>
        (this Func<A, B> f)
    {
        return FuncConvertExtensions.ToFastFunc(f);
    }
...
 

Now that the conversions have been put in place, we can turn our attention to creating the extension methods required for LINQ expressions. 

 

Adding LINQ Extension Methods

In order for LINQ to bind and return data, the SelectMany and Select methods must be implemented.  We need to implement these methods to return an Async<T> class for binding and returning purposes.  As part of the implementation, we need to ensure our Func delegates are converted to the proper FastFunc types.

static class AsyncExtensions
{
    public static Async<B> Select<A, B>(
        this Async<A> x, Func<A, B> selector)
    {
        return async.Bind(x,
            ToFastFunc<A, Async<B>>((r) => async.Return(selector(r))));
    }

    public static Async<V> SelectMany<T, U, V>(
        this Async<T> p, Func<T, Async<U>> selector, Func<T, U, V> projector)
    {
        return async.Bind(p, ToFastFunc<T, Async<V>>(r1 =>
            async.Bind(selector(r1), ToFastFunc<U, Async<V>>(r2 =>
                async.Return(projector(r1, r2))))));
    }
...
 

Once the LINQ methods are added, we can turn our attention on how we might actually implement the asynchronous behavior with our given classes. 

 

Adding Extensions Building Primitives

In order for your classes to use the asynchronous behavior, we need to expose ways of building primitives so that we can enlist those methods that expose the Beginxxx and Endxxx signature which includes the IAsyncResult.  Also, in order to support method calls that do not, we have the ability within the asynchronous computation expressions to unblock via a new thread.

First, we need to define the method which allows us to do non-blocking calls on methods that do not support the Begin/End pattern.  In order for this to happen, we need to call an internal method to the FileExtensions class in the FSharp.PowerPack.dll assembly called UnblockViaNewThread.  Due to the fact that it is a generic method, we have to do special binding using reflection.    Once this has been created, we can now enlist functions that follow the Func<Res> signature.

Second, we need to define methods to build primitives to allow for methods that follow the Begin/End pattern using the IAsyncResult and AsyncCallback delegate.  Overloads are necessary due to the fact that methods may have return types or not.  Examples of this pattern are Stream.BeginRead, Stream.BeginWrite and so on.

Lastly, we need the ability to start the asynchronous computation expression.  This must be the first statement in any asynchronous computation expression that we write.  Let's look at how the code is implemented.

static class AsyncExtensions
{
    public static Async<T> UnblockViaNewThread<T>(FastFunc<Unit, T> f)
    {
        var type = typeof(FileExtensions);
        var methodInfo = type.GetMethod(
            "UnblockViaNewThread", BindingFlags.NonPublic | BindingFlags.Static);

        var genericArguments = new[] { typeof(T) };
        var genericMethodInfo = methodInfo.MakeGenericMethod(genericArguments);
        return genericMethodInfo.Invoke(null, new[] { f }) as Async<T>;
    }

    public static Async<Unit> BuildVoidPrimitive(
        Func<AsyncCallback, object, IAsyncResult> begin,
        Action<IAsyncResult> end)
    {
        return Async.BuildPrimitive(
            begin.ToTupledFastFunc(), FuncConvert.ToFastFunc(end));
    }

    public static Async<R> BuildPrimitive<R>(
        Func<AsyncCallback, object, IAsyncResult> begin,
        Func<IAsyncResult, R> end)
    {
        return Async.BuildPrimitive(
            begin.ToTupledFastFunc(), end.ToFastFunc());
    }
    public static Async<R> BuildPrimitive<Arg, R>(
        Arg a,
        Func<Arg, AsyncCallback, object, IAsyncResult> begin,
        Func<IAsyncResult, R> end)
    {
        return Async.BuildPrimitive(
            a, begin.ToTupledFastFunc(), end.ToFastFunc());
    } 
        
    public static Async<int> StartWorkflow = async.Return(0);
...

 
Now that we have the ability to add asynchronous behavior to our classes, let's implement some extension methods that encompass the behavior.

 

Extending Types With The Begin/End Pattern

For methods that have the standard Begin/End methods can use the BuildPrimitive methods that we defined above.  As our first example, let's implement an asynchronous WebRequest.GetResponse.  Normally in our .NET code, we have to implement the Begin/End ourselves.  Instead, we pass the methods to the BuildPrimitive method to bind.

public static class WebExtensions

    public static Async<WebResponse> AsyncGetResponse
        (this WebRequest request)
    {
        return AsyncExtensions.BuildPrimitive<WebResponse>(
            request.BeginGetResponse, request.EndGetResponse);
    }
}
 

Any method signature that follows this pattern should be able to partake.  Now what about those methods that do not follow the Begin/End pattern.  What can we do about those?

 

Extending Types with Non-Blocking Threads

As I mentioned previously, I want the ability to perform asynchronous operations on those methods that do not follow the Begin/End pattern.  In order to do that, I must use the UnblockViaNewThread method that was defined above.  For example, we could expose the ability to open files asynchronously as either readers or streams.  Let's define some methods to open files asynchronously.

public static class FileSystem
{
    public static Async<FileStream> AsyncOpen(
        string path, FileMode mode, FileAccess access, FileShare share)
    {
        Func<FileStream> f = () => 
            File.Open(path, mode, access, share);
        return AsyncExtensions.UnblockViaNewThread(FuncConvertExtensions.ToFastFunc(f));
    }

    public static Async<FileStream> AsyncOpenRead(string path)
    {
        Func<FileStream> f = () => File.OpenRead(path);
        return AsyncExtensions.UnblockViaNewThread(FuncConvertExtensions.ToFastFunc(f));
    }

    public static Async<StreamReader> AsyncOpenText(string path)
    {
        Func<StreamReader> f = () => File.OpenText(path);
        return AsyncExtensions.UnblockViaNewThread(FuncConvertExtensions.ToFastFunc(f));
    }
}
...
 

This pattern would also apply to such things as WebRequest.Create and so on.  Now, let's bring it all together.

 

Bringing It All Together

Now that we have defined the extension methods and other asynchronous methods, let's tie it all back together.  First, let's look at a quick example of an asynchronous computation expression that will retrieve the number of hyperlinks from a web page.
#light

open System.IO
open System.Net
open System.Text.RegularExpressions

let get_links html = 
  let linkRegex = new Regex(
                    @"<a\s{1}href=\""(?<url>.*?)\""(\s?target=\""" +
                    @"(?<target>_(blank|new|parent|self|top))\"")?" +
                    @"(\s?class=\""(?<class>.*?)\"")?(\s?style=\""" +
                    @"(?<style>.*?)\"")?>(?<title>.*?)</a>");
  linkRegex.Matches(html) |> Seq.cast<Match>

let links_async (requestUriString:string)
  async {
          let request = WebRequest.Create(requestUriString)
          let! response = request.AsyncGetResponse()
          let reader = new StreamReader(response.GetResponseStream())
          let! html = reader.AsyncReadToEnd()
          let links = get_links html
          return requestUriString, links |> Seq.length 
        }

let sites = ["http://live.com/"
             "http://www.google.com/"
             "http://codebetter.com/"]
let results = Async.Run(
                Async.Parallel [for site in sites -> links_async site])
results |> Seq.iter(
  fun result -> printfn "Site %s has %d links" (fst result) (snd result))

What the above code does for me is allows me to count the number of hyperlinks, given a URL, in parallel.  This is a pretty simplistic example, yet works quite well for this demonstration.  Now, using our C# implementation, let's take the above code and get it to work in C#.

Func<string, IEnumerable<Match>> get_links = html =>
{
    var linkRegex = new Regex(
        @"<a\s{1}href=\""(?<url>.*?)\""(\s?target=\""" +
        @"(?<target>_(blank|new|parent|self|top))\"")?" +
        @"(\s?class=\""(?<class>.*?)\"")?(\s?style=\""" +
        @"(?<style>.*?)\"")?>(?<title>.*?)</a>");
    return linkRegex.Matches(html).Cast<Match>();
};

Func<string, Async<Tuple<string, int>>> links_async = requestUriString =>
    from _ in AsyncExtensions.StartWorkflow
    let request = WebRequest.Create(requestUriString)
    from response in request.AsyncGetResponse()
    let reader = new StreamReader(response.GetResponseStream())
    from html in reader.AsyncReadToEnd()
    let links = get_links(html)
    select new Tuple<string, int>(requestUriString, links.Count());

var sites = new[]
    {
        "http://live.com/",
        "http://www.google.com/",
        "http://codebetter.com/"
    };
var results = Async.Run(
    Async.Parallel(from site in sites select links_async(site)), 
        Option<int>.None,
        Option<bool>.None);
foreach (var result in results) 
    Console.WriteLine("Site {0} has {1} links", result.Item1, result.Item2);
 

For each of the above, we get the following result:

get_links

Well, that works exactly according to plan.  But, using this technique, are there any downsides?

 

Downsides?

The asynchronous code I wrote above is pretty simple and naive.  Counting links in an HTML page is a pretty simple example with some asynchronous calls.  But, this breaks down easily if it gets more complex than this.  There are constructs that can be done in the asynchronous computation expressions in F# that cannot be done through LINQ.  For example, there are several things that cannot be done through LINQ expressions such as the following:

  • if/then/else
  • while loops
  • try/catch/finally
  • methods that return void

These above constructs don't have a 1 to 1 mapping with LINQ.  As a result, our code may have to look significantly different than the F# code that we'd write.  That of course eliminates some of the savings we may have had with our LINQ implementation. 

#light

open Microsoft.FSharp.Control

let agent = MailboxProcessor.Start(fun inbox ->
  let rec loop n =
    async {
            do printfn "n = %d" n
            let! message = inbox.Receive()
            if n % 2 = 0 then
              do printfn "message %d is an even number" message
            else
              do printfn "message %d is an odd number" message
            return! loop(n + message)
          }
  loop 0)

As you see, when combined with such things as the MailboxProcessor, the asynchronous computation expressions become a bit more powerful.  Unfortunately, LINQ doesn't support these features of conditionals, using statements and so on.  The alternative being, using the awkward syntax with converting between FastFunc and Func delegates, which negates any savings we may have had.

 

Wrapping It Up

As I've shown in the past posts, LINQ expressions could be used to your advantage to do more than just standard query operations.  Instead, if we start to think of them as very powerful monadic constructs, we can then think of better uses for them.  Although the LINQ expressions are limited in what they can achieve through the asynchronous computation expressions, it still gets you quite far.  I hope this exploration gets you to understand languages such as Haskell and how monads are useful.  For further information, you should check out "What is a monad, why should I use it and when it is appropriate" on Lambda the Ultimate.

The code from this post is available, as always from the Functional C# Library on MSDN Code Gallery.



kick it on DotNetKicks.com

8 Comments

  • That was a really informative post - thanks! Do you imagine C# developers will begin to use LINQ more for its general monadic advantages one day? It seems that even non-Haskellers could develop a broad appreciation for it soon.

  • @Chance

    Glad you liked it.

    That is my hope, to expose that in fact, LINQ exposes the binding capability to be able to express monadic expressions. The point of the past couple of posts was just around that, and it's a little bit of a chasm to cross at this point.

    Using Async Computation Expressions is one of the first really big and relevant steps in explaining the power of monads to the general .NET audience.

    Matt

  • The picture is lost :(

  • Pervasives.async is not available in 2009 May CTP, Could not find an easy way to obtain an AsyncBuilder. Any advice? Thanks

  • @WH:

    It's now in the ExtraTopLevelOperators class.

    Matt

  • And how does that mean? I do not understand anything.

  • Magnificent items from you, man. I've keep in mind your stuff prior to and you're
    simply too magnificent. I actually like what you have received here, really like what you are saying
    and the best way in which you assert it. You're making it enjoyable and you continue to take care of to stay it sensible. I cant wait to learn much more from you. This is actually a wonderful site.

  • It's actually very complicated in this active life to listen news on Television, therefore I only use web for that reason, and get the most up-to-date news.

Comments have been disabled for this content.