Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Agent/MailboxProcessor in C# using new async/await

This question combines two topics I don't fully understand

Reading through a paper about async in F#, I came across the topic of Agents/MailboxProcessors, which can be used to implement reactive state machines. Could the new async/await functionality in C#5 be used to implement something similar in C#, or is there already something analogue that would be better suited?

like image 311
Benjol Avatar asked Nov 02 '10 05:11

Benjol


2 Answers

With a bit of pretty horrible hacking, you can use the MailboxProcessor type from C# using async. Some difficulties are that the type uses some F# specific features (optional arguments are options, functions are FSharpFunc type, etc.)

Technically, the biggest difference is that F# async is dealyed while C# async creates a task that is already running. This means that to construct F# async from C#, you need to write a method that takes unt -> Task<T> and creates Async<T>. I wrote a blog post that discusses the difference.

Anwyay, if you want to experiment, here is some code you can use:

static FSharpAsync<T> CreateAsync<T>(Func<Task<T>> f)
{ 
  return FSharpAsync.FromContinuations<T>(
    FuncConvert.ToFSharpFunc<
      Tuple< FSharpFunc<T, Unit>, 
             FSharpFunc<Exception, Unit>,
             FSharpFunc<OperationCanceledException, Unit> >>(conts => {
    f().ContinueWith(task => {
      try { conts.Item1.Invoke(task.Result); }
      catch (Exception e) { conts.Item2.Invoke(e); }
    });
  }));
}

static void MailboxProcessor() {
  var body = FuncConvert.ToFSharpFunc<
                FSharpMailboxProcessor<int>, 
                FSharpAsync<Unit>>(mbox =>
    CreateAsync<Unit>(async () => {
      while (true) {
        var msg = await FSharpAsync.StartAsTask
          ( mbox.Receive(FSharpOption<int>.None), 
            FSharpOption<TaskCreationOptions>.None, 
            FSharpOption<CancellationToken>.None );
        Console.WriteLine(msg);
      }
      return null;
    }));
  var agent = FSharpMailboxProcessor<int>.Start(body,
                FSharpOption<CancellationToken>.None);
  agent.Post(1);
  agent.Post(2);
  agent.Post(3);
  Console.ReadLine();
}

As you can see, this looks really horrible :-).

  • In principle, it could be possible to write a C# friendly wrapper for the MailboxProcessor type (just extract the ugly bits from this code), but there are some problems.

  • In F# you often use tail-recursive asyncs to implement the state machine in the mailbox processor. If you write the same thing in C#, you'll eventually get StackOverflow, so you'd need to write loops with mutable state.

  • It is perfectly possible to write the agent in F# and call it from C#. This is just a matter of exposing C#-friendly interface from F# (using the Async.StartAsTask method).

like image 81
Tomas Petricek Avatar answered Nov 08 '22 06:11

Tomas Petricek


In principle, I expect it would be straightforward to translate these F# APIs into C#-plus-async-await.

In practice, I am unclear if it would come out beautiful, or ugly and full of extra type annotations, or simply un-idiomatic and in need of some API-massaging to make it feel more at home in C#. I think the jury is out until someone does the work and tries it. (I presume there is no such sample in the await CTP.)

like image 39
Brian Avatar answered Nov 08 '22 07:11

Brian