Our journey to F#: making async understand Tasks

In our F# code, we have a lot of asynchronicity: query the DB, call external services, write messages to the service bus, etc. Inside of our happy F# bubble we use async workflows, either by using Async or by using the computation expression async { ... }. But we are surrounded by a world of Tasks. So we often need to call functions or methods that return a Task. Of course, that is no big problem, we can just add |> Async.AwaitTask, but that leads to a lot of visual clutter in our precious business logic code. And it’s very easy to extend the async { } computation expression to do it for us.

Instead of writing

let foo () =
    async {
        let! bar = Bar.get () |> Async.AwaitTask
        // do some stuff
        return result;
    } 

we would like to write

let foo () =
    async {
        let! bar = Bar.get ()
        // do some stuff
        return result;
    }

because the noise of switching from Task to Async disappears.

To achieve this, we added the following module to our code base:

[<AutoOpen>]
module Calitime.TimeRocket.AsyncCE

open System.Threading.Tasks

/// extends the async CE to allow awaiting tasks
type AsyncBuilder with
    member x.Bind(task : Task<'a>, f : 'a -> Async<'b>) =
        let task = task |> Async.AwaitTask
        async.Bind(task, f)

The module above simply extends the AsyncBuilder with an additional method that handles banging (!) tasks.

FSharp.Control.FusionTasks

Andrii Chebukin pointed me to the library FSharp.Control.FusionTasks, which does the above and much more regarding the combination of async and task. So check it out!


Find all blog posts about our journey to F# here.

This blog post is made possible with the support of Time Rocket, the product this journey is all about. Take a look (German only).

About the author

Urs Enzler

3 comments

By Urs Enzler

Recent Posts