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
        | _ -> ()

3 Answers 3

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
Sign up to request clarification or add additional context in comments.

Comments

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.

3 Comments

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

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

match reason with
| CloseReason.Shutdown -> context.CheckpointAsync() :> Task
| _ -> Task.CompletedTask

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.