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 Task
s. 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).
Why not to use Fusion.Tasks NuGet?
I didn’t know this library until now! Thanks for the link.
Overloading _.Bind() works, but causes some confusion when binding a mixed bag of asyncs and tasks in the same CE in my experience. I found that using _.Source() is much more effective.
Here is how I did it in my Async extensions module 🙂
https://github.com/jamil7/FSharp.Prelude/blob/9d0a58b2b5c133be20469f201cbd9060a298f61d/src/Extensions/Async.fs#L120