1

How do I use F#'s built-in support for async operations classes exposing the Event-based Asynchronous Pattern such as WebClient class?

let Download(url : Uri) =
 let client = new WebClient()
 let html = client.DownloadString(url)
 html

When I try to change this to use "let!" in an async block (say as described in Soma's recent post)

let Download(url : Uri) =
 async {
 let client = new WebClient()
 let! html = client.DownloadStringAsync(url)
 return html }

I get an error message:

Type constraint mismatch. The type unit is not compatible with type Async<'a> The type 'unit' is not compatible with the type 'Async<'a>'

Edit: I'm really asking about the general question of using *Async() methods, WebClient is just an easy example. Microsoft says "... you should expose asynchronous features using the Event-based Asynchronous Pattern [ as opposed to BeginFoo()/EndFoo() ] whenever possible ..." so I would think there should be an easy way to consume an arbitrary *Async() method from F#.

asked Oct 12, 2009 at 16:10

1 Answer 1

5

The WebClient.DownloadStringAsync method is part of the .NET framework. It'll raise an event to signal its progress, and its return type is unit, so you don't want to use it, and there's no advantage in wrapping it in an async object.

The F# PowerPack defines an extension method, val webclient.AsyncDownloadString : uri -> Async{string}:

let Download(url : Uri) =
 async {
 let client = new WebClient()
 client.Encoding <- Encoding.GetEncoding("utf-8")
 let! html = client.AsyncDownloadString(url)
 return html }

Unfortunately, the choice of name clashes with the existing webclient method, which can understandably cause confusion. However, I believe all of the F# async extensions begin with Async*.


[Edit to add in response to comments:]

Usually, .NET uses the BeginFoo / EndFoo pattern for concurrency. If the types are right, you can just use Async.BuildPrimitive beginMethod endMethod, which will return an Async wrapper for the method.

Sometimes objects don't use this pattern, like the WebClient, and you actually have to use Async.AwaitEvent to wait for an event to be fired, or write your own loop to repeatedly check to see if a bool is set. Here's a nice article on converting events to Async objects.

For what its worth, if you have F# installed, you should also have the source code which will give you an idea of how the F# team implements their async extensions. On my machine, the relevant file is located at:

C:\Program Files\FSharp-1.9.6.16\source\fsppack\FSharp.PowerPack\AsyncOperations.fs
answered Oct 12, 2009 at 16:27
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.