Agent/MailboxProcessor in C# using new async/await

后端 未结 3 959
甜味超标
甜味超标 2020-12-30 11:28

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,

相关标签:
3条回答
  • 2020-12-30 12:19

    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.

    0 讨论(0)
  • 2020-12-30 12:31

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

    0 讨论(0)
  • 2020-12-30 12:34

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

    0 讨论(0)
提交回复
热议问题