3

I have to implement a C# interface with F#. The interface and many of the methods I have to use are all written with C# and use async very heavily.

Example code to port from C# to F#:

public async Task CloseAsync(PartitionContext context, CloseReason reason)
{
    Console.WriteLine(string.Format("Processor Shuting Down.  Partition '{0}', Reason: '{1}'.", this.partitionContext.Lease.PartitionId, reason.ToString()));
    if (reason == CloseReason.Shutdown)
    {
        await context.CheckpointAsync();
    }
}

This can be problematic, as F# and C# do not use the same async pattern or types. The function above is part of an interface that I need to implement. My issue comes in how do I await context.CheckpointAsync() with F# in such a way that it still returns a Task.

This is the direction I was attempting to go, however it does not work. context.CheckpointAsync returns Task<a> not Task. Additionally, what about the other cases, in which no operation is expected?

interface IEventProcessor with
    member this.CloseAsync(context:PartitionContext, reason:CloseReason) =
        match reason with
        | CloseReason.Shutdown -> context.CheckpointAsync() |> Async.AwaitTask
        | _ -> ()
ildjarn
  • 62,044
  • 9
  • 127
  • 211
David Crook
  • 2,722
  • 3
  • 23
  • 49

3 Answers3

1

Task is also an IAsyncResult which means you can use Async.AwaitIAsyncResult and ignore the output.

Wrap it all in an async computation expression, start it as a Task<unit> then cast to Task.

interface IEventProcessor with

    member __.CloseAsync(context : PartitionContext, reason : CloseReason) = 
        async { 
            if reason = CloseReason.Shutdown then 
                do! context.CheckpointAsync() |> Async.AwaitIAsyncResult |> Async.Ignore
        }
        |> Async.StartAsTask :> Task
TWith2Sugars
  • 3,384
  • 2
  • 26
  • 43
0

The problem is that in C# this has a return type of Task rather than Task<T>. In a perfect world C# could have Task<void>.

F#3.x has built in bindings for going from Task<T> to Async<T> but not from pla Task to Async<T>. There are samples of that on SO e.g. How to Async.AwaitTask on plain Task (not Task<T>)?

At any rate, it would be helpful to see the exact error you get but here looks like your error is that you're trying to Async.AwaitTask (which will return Async<T>) yet the interface must be Task. So you can just get rid of the Async.AwaitTask() completely.

Then you're going to get another error that the match cases don't tie up i.e. the Shutdown branch returns Task, whereas the other branch returns unit. You need to change that to return a Task as well.

Community
  • 1
  • 1
Isaac Abraham
  • 3,422
  • 2
  • 23
  • 26
  • This seems like cheating... member this.CloseAsync(context:PartitionContext, reason:CloseReason) = match reason with | CloseReason.Shutdown -> context.CheckpointAsync() | _ -> let a = new System.Action(fun t -> ()) new Task(a) – David Crook Mar 09 '15 at 14:40
  • On this note, I got OpenAsync to work, but having issues with ProcessEventsAsync and CloseAsync. The magic wiring doesn't appear to be working. Open async returns a task, but is not marked with the async prefix like Close and Process are. – David Crook Mar 09 '15 at 18:37
  • Why is that cheating? BTW - it won't be marked with the async modifier; AFAIK F# won't generate methods with that modifier. And you're not doing any async code inside this method anyway - all you're doing is returning a task from a child method. There's no awaiting or let! anywhere. – Isaac Abraham Mar 09 '15 at 22:31
0

You could avoid using async and return the task from CheckpointAsync directly:

match reason with
| CloseReason.Shutdown -> context.CheckpointAsync() :> Task
| _ -> Task.CompletedTask
Lee
  • 142,018
  • 20
  • 234
  • 287