12

What are the different techniques you can use to create instances of Async<'T> in F#?

I see there are a number of extension methods for web client/request and file stream, but if I want to write my own provider of async computations, how would I go about writing those AsyncDoSomething versions of my synchronous DoSomething functions?

I know that you can use a delegate of the same signature to wrap the original function, and then use Async.FromBeginEnd on the BeginInvoke and EndInvoke methods:

open System

let del : Func<int> = new Func<int>(fun () -> 42)
let delAsync = async {
    let! res = Async.FromBeginEnd(del.BeginInvoke, del.EndInvoke)
    printfn "result was %d" res
}

Async.Start delAsync

But this feels a little forced and it doesn't seem to be the 'F# way' as you have to use delegates defined in C# or VB (of which there are plenty of System.Action and System.Func variants to choose from of course) because F# delegates don't support the BeginInvoke and EndInvoke methods.

Does anyone have a list of the different ways you can write an async version of a synchronous function in F#?

Many thanks in advance!

2
  • 1
    Making an async out of a sync impl doesn't make much sense IMHO. Commented Dec 22, 2011 at 14:42
  • True, it's mostly useful when you need to do some IO work (so async lets you free up an otherwise blocked thread in the sync version), which are covered by the F#'s common extension methods already, but I'm just interested in 'how' you'd go about doing that. Maybe one day you'll come across some method that performs IO work but doesn't come with a Begin/End pair, for instance. Commented Dec 22, 2011 at 14:52

3 Answers 3

6

From the docs for Async, all the AwaitXXX and FromXXX methods. But the most common way is using asynchronous workflows. However, as Mauricio commented, wrapping arbitrary code with async { } isn't always beneficial.

UPDATE

Here's a bit of code to demonstrate that point.

open System.IO

let BUF_SIZE = 1 <<< 16 //64KB

let readFile f (stream:Stream) =
  let buf = Array.zeroCreate BUF_SIZE
  let rec read p =
    async {
      let! n = f stream buf 
      match n with
      | 0 -> ()
      | _ -> return! read (p + n)
    }
  read 0

let fakeAsyncReadFile s = readFile (fun stream buf -> 
  async { return stream.Read(buf, 0, buf.Length) }) s

let realAsyncReadFile s = readFile (fun stream buf -> 
  stream.AsyncRead(buf, 0, buf.Length)) s

let files = [@"C:\big_file_1"; @"C:\big_file_2"]

let readWith f = 
  let streams = Seq.map File.OpenRead files
  try Seq.map f streams |> Async.Parallel |> Async.RunSynchronously |> ignore
  finally streams |> Seq.iter (fun s -> s.Close())

readWith fakeAsyncReadFile //Real: 00:00:34.190, CPU: 00:00:03.166, GC gen0: 4, gen1: 2, gen2: 1
readWith realAsyncReadFile //Real: 00:00:05.101, CPU: 00:00:16.957, GC gen0: 31, gen1: 1, gen2: 0

Wrapping the synchronous Stream.Read with async { } yields no observable benefits. An async workflow is primarily a convenient way to chain asynchronous operations. That is, it depends on having well-written asynchronous operations to start with, to serve as building blocks.

Sign up to request clarification or add additional context in comments.

4 Comments

can you elaborate on the use of async { }? The let! binding requires an instance of Async<'T> on the right-hand side of the expression, and it's the creation of those Async<'T> instances I'm interested in.
AwaitEvent and AwaitTask can be useful if you've got a task or event at hand i guess.
so something like this? let delAsync2 = async { return 42 } printfn "result was %d" <| Async.RunSynchronously delAsync2
Don Syme's blog has some examples.
3

You could get some good mileage from Async.FromContinuations. That function allows you to define an Async from continuation functions. If you look at the definition of WebClient's AsyncDownloadString, you'll see this in use.

Comments

3

And you can write like this:

let doAsyncTask  (f : unit->'a) = 
     async { return! Task<'a>.Factory.StartNew( new Func<'a>(f) ) |> Async.AwaitTask }

and use it like

let asyncFunc arg = async { return! doAsyncTask( fun () -> syncFunc arg ) } 

Comments

Your Answer

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.