I recently started a new job where all of our core development is done in F#. It’s definitely been culture shock for me as I’ve come from a C# background. Moving from an object-oriented language to a functional language has been manageable, but not without difficultly. One of the concepts that I had some trouble understanding at first was asynchronous computation expressions (the async keyword). Hopefully this post can help some others understand the concept.
If you haven’t guessed yet, an asynchronous computation expression is a piece of F# code that represents some work that you would like to perform asynchronously. Typically you would use one when you have some code that you would like to run asynchronously such as writing to a database or reading some data over a network. In these cases, the asynchronous computation expression can be put on hold while I/O is being performed to allow other code to execute. Pretty simple concept right?
So, what does an asynchronous computation expression look like? Take a look at the sample code below:
The code above wraps the process of downloading a URL into an asynchronous computation expression. We first create the necessary objects to pass to the DownloadDataTaskAsync function. Then we create an Async by piping the output of DownloadDataTaskAsync to Async.AwaitTask. We evaluate this using the let! syntax which will execute our Async and return to us a byte object containing the data of the web page we downloaded. Note that let! will run the the operation asynchronously without blocking the main thread of the application. Finally, we return the data object out of the expression.
So, now that we have a function that returns an Async, how can we actually execute it? There are two options that I know of. You can use this expression from within another expression and retrieve the result with the let! syntax. If you plan on using it outside of an asynchronous computation expression, you can use methods within the Control.Async class. The simplest function to use is probably the Async.RunSynchronously function which will start the expression and block until it is complete. An example of using this function is below:
The code above simply executes the “download” function to retrieve the computation expression and runs it immediately and waits for the result by piping the output to Async.RunSynchronously. We then send this output to the “outputResult” function to print out what has been downloaded.
That’s really all there is to understanding the basics of using asynchronous computation expressions. In a future post, I’ll go over how to use multiple expressions in sequence to keep your applications responsive during I/O and other long-running operations.