F# computation expressions and the Task Parallel Library

478 Views Asked by At

I've been working a lot with F# lately, and recently wrote a little MVC application with a C# web project and an F# class library, the latter of which contained the bulk of the code.

I wanted to use the async controllers feature of the later versions of MVC where you can return a Task<ActionResult>. I was writing my code using F# asynchronous computation expressions but was getting very irritated having to constantly convert from Async<'T> to Task<'T> using Async.AwaitTask and StartAsTask.

Since I was learning computation expressions at the time, I thought "There must be a way to use computation expressions with Tasks as well as Async to allow better F#/C# interop", so I had a go:

type TaskBuilder () =
    member this.ReturnFrom task = task
    member this.Return value = Task.FromResult value
    member this.Bind (task : Task<'a>, continuation: 'a -> Task<'b>) =
        task.ContinueWith(fun (t : _ Task) ->
            (t.Result |> continuation).Result)
    member this.Zero () = Task.FromResult ()

This appears to work; running the following code:

let task = TaskBuilder()

task {
    let! data = (new WebClient()).DownloadStringTaskAsync(Uri "http://www.google.com")

    printfn "Downloaded from Google: %A" (data.Length)
}
|> ignore

task {
    let! data = (new WebClient()).DownloadStringTaskAsync(Uri "http://www.bing.com")

    printfn "Downloaded from Bing: %A" (data.Length)
}
|> ignore

printfn "Hello world"

yields the result:

Here
Downloaded from Bing: 39121
Downloaded from Google: 46005

It gives the appearance of asynchrony, and you end up with the correct type if you return from the computation expression, so I'm cautiously labelling this a success.

The thing that makes me suspicious is that I couldn't find any examples of other people trying this, so I'd appreciate some outside perspective on

  1. Has this worked?
  2. Is this a good idea in the first place?
0

There are 0 best solutions below