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
- Has this worked?
- Is this a good idea in the first place?