8

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?

Benjol
  • 63,995
  • 54
  • 186
  • 268

3 Answers3

11

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).

ChaosPandion
  • 77,506
  • 18
  • 119
  • 157
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • Ouch, my eyes :) The `Task -> Async` was precisely the bit that I got stuck on, so I'll have a look at that. How about rewriting a MailboxProcessor in C#, or would it just not be worth the effort because of your second bullet point about recursion? – Benjol Nov 02 '10 at 13:53
  • @Benjol - I wrote a similar type in C#. It was about 300 lines of code. The difference being a simplified API. – ChaosPandion Nov 02 '10 at 13:56
  • @Benjol: I think that rewriting MailboxProcessor in C# would be an option too. It is not easy to get the locking and synchronization right (especially for `TryScan` method), but I think it should work (You can always avoid recursion, but in case of _state machines_ the code looks quite bad, so it is unfortunate if that's what users have to write) – Tomas Petricek Nov 02 '10 at 13:57
3

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.)

Brian
  • 117,631
  • 17
  • 236
  • 300
0

You might nave a look at Stact. It hasn't been updated in a little while, but if you wanted to make something with a little better C# support, you might find it a good starting point. I don't think it's up-to-date with async/await, though.