TP

Asynchronous C# and F# (I.): Simultaneous introduction

One of the exciting new technologies that was announced at PDC 2010 is the support for asynchronous programming in C#. So what exactly is asynchronous programming? Many applications today need to connect to some service or external data source including, for example, Web Services and REST APIs. This calls may take a long time to run, but we want to run them without blocking the application.

Currently, you can run the operation on a background thread or using a Task, but coordinating multiple such operations is difficult. What if you for example need to wait until any (or all) of downloads complete and run some more code then? This is not only difficult, but it also scales badly, because blocking .NET threads is a bad practice (Threads are expensive and when they're just waiting for other operation to complete, we're wasting valuable resources). This problem has been the main motivation for including asynchronous workflows in F# about 3 years ago. In F#, this also enabled various interesting programming styles - for example creating GUI using asynchronous workflows (also discussed in Chapter 16 of my book and in in my recent talk). The C# asynchronous programming support and the await keyword is largely inspired by F# asynchronous workflows (I was quite surprised that F# wasn't more visibly mentioned in the PDC talk).

In this article series, I'll demonstrate both F# and C# asynchronous programming model, I'll look at features that are missing in one or the other as well as a few subtle differences (that may be unexpected) and finally, I'll write about writing (asynchronous) F# libraries that are easy to use from C#. The plan for the series is following:

Let's start with a brief overview of asynchronous programming features in C# and F#...

Introducing C# await construct

Perhaps the best example to demonstrate asynchronous programming on .NET is implementing a method to download the content of a web page. In practice, this can be done easily using WebClient, but it works well as a demo. In the implementation, we need to get HTTP response and then read data from the stream repeatedly in a loop into a buffer.

Writing the code using BeginGetResponse and BeginRead methods is quite difficult. We can pass lambda function as an argument, but there isn't an easy way of implementing the looping. The problem is that the iteration is started in one scope, but continues in a different scope (the body of the lambda). As a result, we cannot use any standard control-flow constructs such as while, for but also try ... catch.

Downloading page asynchronously

If the feature makes it into C# 5.0 (which I would expect) then we'll be able to write the same thing more easily using the await construct. The following snippet shows the initialization of the HTTP request - we'll discuss the actual downloading in the next snippet.

async Task<Tuple<string, int>> DownloadPage(string url) {
  // Create web request to download a web site
  var request = HttpWebRequest.Create(url);
  // Asynchronously obtain the HTTP response
  using (var response = await request.GetResponseAsync()) {
    // Start reading the response stream
    using (var stream = response.GetResponseStream()) {
      // TODO: Asynchronously copy content of the stream
      // TODO: Read 'html' content and extract 'title'
      return Tuple.Create(title, html.Length);
    }
  }
}

The method creates a web request and then obtains a HTTP response from the server. It uses the usual using statement to ensure that all created objects will be disposed. At the first look, it looks like a completely ordinary C# method, but it is not. There are two important differences:

The two changes above turn a usual method into an asynchronous method. When called, the asynchronous method starts running, but returns a task before it completes. The caller can then do some other work before waiting for the result of the call. The waiting is done using the new await construct. The construct can appear almost anywhere in an expression and we can use it to obtain the result of a Task<T> or to wait until Task completes.

Blocking and non-blocking waiting

There is an important difference between writing task.Result and await task. In the first case, the Result property simply blocks the thread until the task completes and produces result. On the other hand, the await construct is handled in a special way by the C# compiler. The compiler translates the method into a state machine and replaces all uses of await with code that sets a continuation of a task and returns. A continuation is simply a function that will be called when the task completes and will continue running the body of the method. This requires quite a bit of reorganization in the body of the method and it is not something that can be easily done by hand. The key point is that using await will not block any thread.

Let's now look at the second snippet. It shows a loop that asynchronously downloads the page content using 1kB buffer. When it completes, it extracts title of the page and returns it together with the length of the page:

// Regular expression for extracting page title
Regex regTitle = new Regex(@@"\<title\>([^\<]+)\</title\>");

// Create a buffer and stream to copy data to
var buffer = new byte[1024];
var temp = new MemoryStream();
int count;
do {
  // Asynchronously read next 1kB of data
  count = await stream.ReadAsync(buffer, 0, buffer.Length);
  // Write data to memory stream
  temp.Write(buffer, 0, count);
} while (count > 0);

// Read data as string and find page title
temp.Seek(0, SeekOrigin.Begin);
var html = new StreamReader(temp).ReadToEnd();
var title = regTitle.Match(html).Groups[1].Value;
return Tuple.Create(title, html.Length);

Again, this looks like a completely normal C# code with a loop inside. The interesting thing is that we use await inside the loop to implement asynchronous waiting. We start by initializing the buffer and a memory stream where we copy the downloaded data. Inside the loop, we start reading from the HTTP stream into a 1kB buffer. This may take some time, so we use asynchronous version of the operation ReadAsync, which returns Task<int>.

When the reading completes, the system calls a callback that we provided and the callback runs a continuation generated by the C# compiler. The continuation runs the rest of the loop body, checks the condition and then jumps either to the start of the loop (again) or to the code after the loop.

Starting asynchronous operations

Let's now look how we can run the method for a couple of URLs in parallel and collect all the results. The C# language doesn't provide any direct language support for doing that, but we can easily do that using a library. The library that is distributed with the C# preview contains a class TaskEx (I guess it would be merged with standard Task class in a new .NET). The task has a very useful method named WhenAll:

async static Task ComparePages() {
  var urls = new[] { "http://www.google.com",
    "http://www.bing.com", "http://www.yahoo.com", };
  var downloads = urls.Select(DownloadPage);
  var results = await TaskEx.WhenAll(downloads);
  foreach (var item in results)
    Console.WriteLine("{0} (length {1})", item.Item1, item.Item2);
}
static void Main() {
  ComparePages().Wait();
}

The WhenAll method (corresponding to Async.Parallel in F#) takes a collection of Task<T> objects and creates a single task that waits until all the tasks given as an argument complete and returns the results in an array. The return type of the method is Task<T[]>. In the code snippet above, we just create a list of URLs and use the Select method to call DownloadPage for every URL. The result is an array of tasks that are downloading the specified web pages.

Next, we use the await keyword again to wait until the aggregating task completes (meaning that all downloads have completed). As a result, we get an array that contains tuples returned by DownloadPage, so we can simply iterate over the values in the array and print the title of the page with the total size in bytes.

The method ComparePages is again implemented as an asynchronous method (meaning that it is marked with async and returns Task). It doesn't return any result, but it runs in the background and eventually completes and prints the results. Since Main cannot be asynchronous, we cannot call it using await, but we can use the standard Wait method of the Task type to block the program until the operation finishes. It is worth noting that when we call ComparePages, the operation already starts downloading the web pages, so if your application needs to do something else, you can just call the method and discard the returned Task object.

Why do we need asynchronous programming?

You may be wondering why we need asynchronous programming in the previous example at all. Why can't we just create three threads and run every operation on a separate thread. If we did that, an operation would occupy the thread for the entire time needed to download the page. On the other hand, with asynchronous support, a thread is needed only to run the processing code. An operation called using await is handled by the system and it will resume the code (and start using some thread) only after it completes. This means that when using asynchronous model, the above code could easily run just on a single thread, even when downloading larger number of pages such as 50.

Using F# asynchronous workflows

Let's now look at the same problem implemented in F#. Asynchronous programming support is not built directly into the F# language. It is implemented using computation expressions, which is a general purpose feature for writing computations with some non-standard aspect. In F#, the same language feature can be used for sequence expressions (similar to iterators and yield return) and asynchronous workflows.

Downloading page asynchronously

The .NET methods used and the control flow of the F# version is exactly the same. The main difference is that I'm using a recursive function instead of while to implement the looping, but that's just a personal preference. I wanted to show an idiomatic functional solution (instead of an imperative while loop), but you can use the while loop in F# just fine.

 1: let regTitle = new Regex(@@"\<title\>([^\<]+)\</title\>")
 2: 
 3: /// Asynchronously downloads a web page and returns 
 4: /// title of the page and size in bytes
 5: let downloadPage(url:string) = async {
 6:   let request = HttpWebRequest.Create(url)
 7:   // Asynchronously get response and dispose it when we're done
 8:   use! response = request.AsyncGetResponse()
 9:   use stream = response.GetResponseStream()
10:   let temp = new MemoryStream()
11:   let buffer = Array.zeroCreate 4096
12: 
13:   // Loop that downloads page into a buffer (could use 'while' 
14:   // but recursion is more typical for functional language)
15:   let rec download() = async { 
16:     let! count = stream.AsyncRead(buffer, 0, buffer.Length)
17:     do! temp.AsyncWrite(buffer, 0, count)
18:     if count > 0 then return! download() }
19:   
20:   // Start the download asynchronously and handle results
21:   do! download()
22:   temp.Seek(0L, SeekOrigin.Begin) |> ignore
23:   let html = (new StreamReader(temp)).ReadToEnd()
24:   return regTitle.Match(html).Groups.[1].Value, html.Length }F# Web Snippets

It is not difficult to see how this corresponds to the C# version we've seen before. The body of the function is wrapped in an async { ... } block. Unlike in C#, this is not a hard-coded language feature - async is not a keyword but a value defined in F# library that specifies what is special about the code block (in our case, the fact that it is asynchronous). The block constructs a value of type Async<string * int>, which represents an asynchronous computation that can be started and eventually returns a tuple.

The keywords with bang correspond to various uses of the await keyword in C#. We're using use! keyword to start an asynchronous operation and dispose of the returned object when the workflow completes, we're using let! to start an operation and bind the result to a value and finally, we're using do! to start operation that doesn't return any result. I'll discuss some of the differences between F# and C# later (and in more details in an upcoming article). First, let's look how to start the computation...

Starting asynchronous computations

Just like in the C# version, we'll first write a function comparePages that downloads multiple pages in parallel and then prints all the results and returns. We'll write it as an asynchronous workflow representing the composed computation. Note that unlike in C#, the computation is not started yet - we're just building a specification of what should be done. Once we have the workflow, we can start it. In the following example we start it and wait until it completes:

 1: /// Downloads pages in parallel and prints all results
 2: let comparePages = async {
 3:   let! results = 
 4:     [| "http://www.google.com"; "http://www.bing.com"
 5:        "http://www.yahoo.com" |]
 6:     |> Array.map downloadPage
 7:     |> Async.Parallel
 8:   for title, length in results do
 9:     Console.WriteLine("{0} (length {1})", title, length) }
10: 
11: // Start the computation on current thread
12: do comparePages |> Async.RunSynchronouslyF# Web Snippets

The code is again very similar to the C# version earlier in the article. We create an array of URLs, use Array.map to turn it into an array of asynchronous computations and then compose the computations into a single one using Async.Parallel. This creates a composed computation that returns an array with all the results. To get the results we wait until the composed computation completes using let! Once that happens, we iterate over the results and output them to the screen.

Unlike in C# (where asynchronous method returns a started task), the F# version just gives us a specification of the work to be done. We can start it using various different methods. The method used in the example above is Async.RunSynchronously, which starts the workflow and blocks until it completes. However, the sub-tasks done by the workflow (3 distinct downloads) will be all started in parallel.

Comparing C# and F# asynchronous model

Now that we've seen the same problem implemented in both F# and C#, let's look at some of the similarities and differences between the two programming models. I'll write a separate article with more details and discussing some of the subtle and tricky differences, but I'd like to mention at least a few of them in this introduction.

Similarities

Let's start with the similarities. Obviously, the syntax is quite similar. The await keyword corresponds to several keywords from F# computation expressions depending on how it is used. The most common is let! and then also use! and do! These keywords are used to insert a special asynchronous call into code that is otherwise sequential.

There are also some very similar concepts in the libraries, in particular, for introducing parallelism. There are two ways of adding parallelism using asynchronous programming model. One is to compose multiple operations returning the same type (and running the same code) and the other one is to start and wait for tasks explicitly. For the F# way of doing this, see section 4.1 in The F# Asynchronous Programming Model [1] article.

Differences

Even though the programming model looks almost the same, there are some notable differences. I'll write a more detailed article about some of them, but here is an assorted list of those that I found quite important:

Summary

As far as I can tell, the asynchronous programming model in C# is largely based on F# asynchronous workflows. It is wonderful to see how ideas from the F# language spread to the main-stream (although it would be nicer if this was more visibly acknowledged in the presentation).

This definitely doesn't mean that F# is not interesting anymore. There are many useful features in F# not related to asynchronous programming that would be difficult to imagine in C#. Even when talking just about asynchronous programming, F# still has some features and libraries that make it more attractive. Two examples are implicit cancellation and a wonderful library for agent-based parallelism. Moreover, there are interesting new things in F# comming out at PDC 2010. There are also interesting research projects (like my pattern matching extension [3]) which would make F# even better for reactive, concurrent and asynchronous programming (but, just to be clear, that's a distant future).

 

References

val regTitle : Regex

Full name: Demo.regTitle

  type: Regex
  implements: Runtime.Serialization.ISerializable
type Regex =
  class
    new : string -> System.Text.RegularExpressions.Regex
    new : string * System.Text.RegularExpressions.RegexOptions -> System.Text.RegularExpressions.Regex
    member GetGroupNames : unit -> string []
    member GetGroupNumbers : unit -> int []
    member GroupNameFromNumber : int -> string
    member GroupNumberFromName : string -> int
    member IsMatch : string -> bool
    member IsMatch : string * int -> bool
    member Match : string -> System.Text.RegularExpressions.Match
    member Match : string * int -> System.Text.RegularExpressions.Match
    member Match : string * int * int -> System.Text.RegularExpressions.Match
    member Matches : string -> System.Text.RegularExpressions.MatchCollection
    member Matches : string * int -> System.Text.RegularExpressions.MatchCollection
    member Options : System.Text.RegularExpressions.RegexOptions
    member Replace : string * string -> string
    member Replace : string * System.Text.RegularExpressions.MatchEvaluator -> string
    member Replace : string * string * int -> string
    member Replace : string * System.Text.RegularExpressions.MatchEvaluator * int -> string
    member Replace : string * string * int * int -> string
    member Replace : string * System.Text.RegularExpressions.MatchEvaluator * int * int -> string
    member RightToLeft : bool
    member Split : string -> string []
    member Split : string * int -> string []
    member Split : string * int * int -> string []
    member ToString : unit -> string
    static member CacheSize : int with get, set
    static member CompileToAssembly : System.Text.RegularExpressions.RegexCompilationInfo [] * System.Reflection.AssemblyName -> unit
    static member CompileToAssembly : System.Text.RegularExpressions.RegexCompilationInfo [] * System.Reflection.AssemblyName * System.Reflection.Emit.CustomAttributeBuilder [] -> unit
    static member CompileToAssembly : System.Text.RegularExpressions.RegexCompilationInfo [] * System.Reflection.AssemblyName * System.Reflection.Emit.CustomAttributeBuilder [] * string -> unit
    static member Escape : string -> string
    static member IsMatch : string * string -> bool
    static member IsMatch : string * string * System.Text.RegularExpressions.RegexOptions -> bool
    static member Match : string * string -> System.Text.RegularExpressions.Match
    static member Match : string * string * System.Text.RegularExpressions.RegexOptions -> System.Text.RegularExpressions.Match
    static member Matches : string * string -> System.Text.RegularExpressions.MatchCollection
    static member Matches : string * string * System.Text.RegularExpressions.RegexOptions -> System.Text.RegularExpressions.MatchCollection
    static member Replace : string * string * string -> string
    static member Replace : string * string * System.Text.RegularExpressions.MatchEvaluator -> string
    static member Replace : string * string * string * System.Text.RegularExpressions.RegexOptions -> string
    static member Replace : string * string * System.Text.RegularExpressions.MatchEvaluator * System.Text.RegularExpressions.RegexOptions -> string
    static member Split : string * string -> string []
    static member Split : string * string * System.Text.RegularExpressions.RegexOptions -> string []
    static member Unescape : string -> string
  end

Full name: System.Text.RegularExpressions.Regex

  type: Regex
  implements: Runtime.Serialization.ISerializable
val downloadPage : string -> Async<string * int>

Full name: Demo.downloadPage

Asynchronously downloads a web page and returns
 title of the page and size in bytes

val url : string

  type: string
  implements: IComparable
  implements: ICloneable
  implements: IConvertible
  implements: IComparable<string>
  implements: seq<char>
  implements: Collections.IEnumerable
  implements: IEquatable<string>
Multiple items
val string : 'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------

type string = String

Full name: Microsoft.FSharp.Core.string

  type: string
  implements: IComparable
  implements: ICloneable
  implements: IConvertible
  implements: IComparable<string>
  implements: seq<char>
  implements: Collections.IEnumerable
  implements: IEquatable<string>
val async : AsyncBuilder

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
val request : WebRequest

  type: WebRequest
  implements: Runtime.Serialization.ISerializable
  inherits: MarshalByRefObject
type HttpWebRequest =
  class
    inherit System.Net.WebRequest
    member Abort : unit -> unit
    member Accept : string with get, set
    member AddRange : int -> unit
    member AddRange : int * int -> unit
    member AddRange : string * int -> unit
    member AddRange : string * int * int -> unit
    member Address : System.Uri
    member AllowAutoRedirect : bool with get, set
    member AllowWriteStreamBuffering : bool with get, set
    member AutomaticDecompression : System.Net.DecompressionMethods with get, set
    member BeginGetRequestStream : System.AsyncCallback * obj -> System.IAsyncResult
    member BeginGetResponse : System.AsyncCallback * obj -> System.IAsyncResult
    member ClientCertificates : System.Security.Cryptography.X509Certificates.X509CertificateCollection with get, set
    member Connection : string with get, set
    member ConnectionGroupName : string with get, set
    member ContentLength : int64 with get, set
    member ContentType : string with get, set
    member ContinueDelegate : System.Net.HttpContinueDelegate with get, set
    member CookieContainer : System.Net.CookieContainer with get, set
    member Credentials : System.Net.ICredentials with get, set
    member EndGetRequestStream : System.IAsyncResult -> System.IO.Stream
    member EndGetRequestStream : System.IAsyncResult * System.Net.TransportContext -> System.IO.Stream
    member EndGetResponse : System.IAsyncResult -> System.Net.WebResponse
    member Expect : string with get, set
    member GetRequestStream : unit -> System.IO.Stream
    member GetRequestStream : System.Net.TransportContext -> System.IO.Stream
    member GetResponse : unit -> System.Net.WebResponse
    member HaveResponse : bool
    member Headers : System.Net.WebHeaderCollection with get, set
    member IfModifiedSince : System.DateTime with get, set
    member KeepAlive : bool with get, set
    member MaximumAutomaticRedirections : int with get, set
    member MaximumResponseHeadersLength : int with get, set
    member MediaType : string with get, set
    member Method : string with get, set
    member Pipelined : bool with get, set
    member PreAuthenticate : bool with get, set
    member ProtocolVersion : System.Version with get, set
    member Proxy : System.Net.IWebProxy with get, set
    member ReadWriteTimeout : int with get, set
    member Referer : string with get, set
    member RequestUri : System.Uri
    member SendChunked : bool with get, set
    member ServicePoint : System.Net.ServicePoint
    member Timeout : int with get, set
    member TransferEncoding : string with get, set
    member UnsafeAuthenticatedConnectionSharing : bool with get, set
    member UseDefaultCredentials : bool with get, set
    member UserAgent : string with get, set
    static member DefaultCachePolicy : System.Net.Cache.RequestCachePolicy with get, set
    static member DefaultMaximumErrorResponseLength : int with get, set
    static member DefaultMaximumResponseHeadersLength : int with get, set
  end

Full name: System.Net.HttpWebRequest

  type: HttpWebRequest
  implements: Runtime.Serialization.ISerializable
  inherits: WebRequest
  inherits: MarshalByRefObject
Multiple overloads
WebRequest.Create(requestUri: Uri) : WebRequest
WebRequest.Create(requestUriString: string) : WebRequest
val response : WebResponse

  type: WebResponse
  implements: Runtime.Serialization.ISerializable
  implements: IDisposable
  inherits: MarshalByRefObject
member WebRequest.AsyncGetResponse : unit -> Async<WebResponse>
val stream : Stream

  type: Stream
  implements: IDisposable
  inherits: MarshalByRefObject
WebResponse.GetResponseStream() : Stream
val temp : MemoryStream

  type: MemoryStream
  implements: IDisposable
  inherits: Stream
  inherits: MarshalByRefObject
type MemoryStream =
  class
    inherit System.IO.Stream
    new : unit -> System.IO.MemoryStream
    new : int -> System.IO.MemoryStream
    new : System.Byte [] -> System.IO.MemoryStream
    new : System.Byte [] * bool -> System.IO.MemoryStream
    new : System.Byte [] * int * int -> System.IO.MemoryStream
    new : System.Byte [] * int * int * bool -> System.IO.MemoryStream
    new : System.Byte [] * int * int * bool * bool -> System.IO.MemoryStream
    member CanRead : bool
    member CanSeek : bool
    member CanWrite : bool
    member Capacity : int with get, set
    member Flush : unit -> unit
    member GetBuffer : unit -> System.Byte []
    member Length : int64
    member Position : int64 with get, set
    member Read : System.Byte [] * int * int -> int
    member ReadByte : unit -> int
    member Seek : int64 * System.IO.SeekOrigin -> int64
    member SetLength : int64 -> unit
    member ToArray : unit -> System.Byte []
    member Write : System.Byte [] * int * int -> unit
    member WriteByte : System.Byte -> unit
    member WriteTo : System.IO.Stream -> unit
  end

Full name: System.IO.MemoryStream

  type: MemoryStream
  implements: IDisposable
  inherits: Stream
  inherits: MarshalByRefObject
val buffer : byte []

  type: byte []
  implements: ICloneable
  implements: Collections.IList
  implements: Collections.ICollection
  implements: Collections.Generic.IList<byte>
  implements: Collections.Generic.ICollection<byte>
  implements: seq<byte>
  implements: Collections.IEnumerable
  inherits: Array
type Array =
  class
    member Clone : unit -> obj
    member CopyTo : System.Array * int -> unit
    member CopyTo : System.Array * int64 -> unit
    member GetEnumerator : unit -> System.Collections.IEnumerator
    member GetLength : int -> int
    member GetLongLength : int -> int64
    member GetLowerBound : int -> int
    member GetUpperBound : int -> int
    member GetValue : int [] -> obj
    member GetValue : int -> obj
    member GetValue : int64 -> obj
    member GetValue : int64 [] -> obj
    member GetValue : int * int -> obj
    member GetValue : int64 * int64 -> obj
    member GetValue : int * int * int -> obj
    member GetValue : int64 * int64 * int64 -> obj
    member Initialize : unit -> unit
    member IsFixedSize : bool
    member IsReadOnly : bool
    member IsSynchronized : bool
    member Length : int
    member LongLength : int64
    member Rank : int
    member SetValue : obj * int -> unit
    member SetValue : obj * int [] -> unit
    member SetValue : obj * int64 -> unit
    member SetValue : obj * int64 [] -> unit
    member SetValue : obj * int * int -> unit
    member SetValue : obj * int64 * int64 -> unit
    member SetValue : obj * int * int * int -> unit
    member SetValue : obj * int64 * int64 * int64 -> unit
    member SyncRoot : obj
    static member AsReadOnly<'T> : 'T [] -> System.Collections.ObjectModel.ReadOnlyCollection<'T>
    static member BinarySearch : System.Array * obj -> int
    static member BinarySearch<'T> : 'T [] * 'T -> int
    static member BinarySearch : System.Array * obj * System.Collections.IComparer -> int
    static member BinarySearch<'T> : 'T [] * 'T * System.Collections.Generic.IComparer<'T> -> int
    static member BinarySearch : System.Array * int * int * obj -> int
    static member BinarySearch<'T> : 'T [] * int * int * 'T -> int
    static member BinarySearch : System.Array * int * int * obj * System.Collections.IComparer -> int
    static member BinarySearch<'T> : 'T [] * int * int * 'T * System.Collections.Generic.IComparer<'T> -> int
    static member Clear : System.Array * int * int -> unit
    static member ConstrainedCopy : System.Array * int * System.Array * int * int -> unit
    static member ConvertAll<'TInput,'TOutput> : 'TInput [] * System.Converter<'TInput,'TOutput> -> 'TOutput []
    static member Copy : System.Array * System.Array * int -> unit
    static member Copy : System.Array * System.Array * int64 -> unit
    static member Copy : System.Array * int * System.Array * int * int -> unit
    static member Copy : System.Array * int64 * System.Array * int64 * int64 -> unit
    static member CreateInstance : System.Type * int -> System.Array
    static member CreateInstance : System.Type * int [] -> System.Array
    static member CreateInstance : System.Type * int64 [] -> System.Array
    static member CreateInstance : System.Type * int * int -> System.Array
    static member CreateInstance : System.Type * int [] * int [] -> System.Array
    static member CreateInstance : System.Type * int * int * int -> System.Array
    static member Exists<'T> : 'T [] * System.Predicate<'T> -> bool
    static member Find<'T> : 'T [] * System.Predicate<'T> -> 'T
    static member FindAll<'T> : 'T [] * System.Predicate<'T> -> 'T []
    static member FindIndex<'T> : 'T [] * System.Predicate<'T> -> int
    static member FindIndex<'T> : 'T [] * int * System.Predicate<'T> -> int
    static member FindIndex<'T> : 'T [] * int * int * System.Predicate<'T> -> int
    static member FindLast<'T> : 'T [] * System.Predicate<'T> -> 'T
    static member FindLastIndex<'T> : 'T [] * System.Predicate<'T> -> int
    static member FindLastIndex<'T> : 'T [] * int * System.Predicate<'T> -> int
    static member FindLastIndex<'T> : 'T [] * int * int * System.Predicate<'T> -> int
    static member ForEach<'T> : 'T [] * System.Action<'T> -> unit
    static member IndexOf : System.Array * obj -> int
    static member IndexOf<'T> : 'T [] * 'T -> int
    static member IndexOf : System.Array * obj * int -> int
    static member IndexOf<'T> : 'T [] * 'T * int -> int
    static member IndexOf : System.Array * obj * int * int -> int
    static member IndexOf<'T> : 'T [] * 'T * int * int -> int
    static member LastIndexOf : System.Array * obj -> int
    static member LastIndexOf<'T> : 'T [] * 'T -> int
    static member LastIndexOf : System.Array * obj * int -> int
    static member LastIndexOf<'T> : 'T [] * 'T * int -> int
    static member LastIndexOf : System.Array * obj * int * int -> int
    static member LastIndexOf<'T> : 'T [] * 'T * int * int -> int
    static member Resize<'T> : 'T [] * int -> unit
    static member Reverse : System.Array -> unit
    static member Reverse : System.Array * int * int -> unit
    static member Sort : System.Array -> unit
    static member Sort<'T> : 'T [] -> unit
    static member Sort : System.Array * System.Array -> unit
    static member Sort : System.Array * System.Collections.IComparer -> unit
    static member Sort<'TKey,'TValue> : 'TKey [] * 'TValue [] -> unit
    static member Sort<'T> : 'T [] * System.Collections.Generic.IComparer<'T> -> unit
    static member Sort<'T> : 'T [] * System.Comparison<'T> -> unit
    static member Sort : System.Array * int * int -> unit
    static member Sort : System.Array * System.Array * System.Collections.IComparer -> unit
    static member Sort<'T> : 'T [] * int * int -> unit
    static member Sort<'TKey,'TValue> : 'TKey [] * 'TValue [] * System.Collections.Generic.IComparer<'TKey> -> unit
    static member Sort : System.Array * System.Array * int * int -> unit
    static member Sort : System.Array * int * int * System.Collections.IComparer -> unit
    static member Sort<'TKey,'TValue> : 'TKey [] * 'TValue [] * int * int -> unit
    static member Sort<'T> : 'T [] * int * int * System.Collections.Generic.IComparer<'T> -> unit
    static member Sort : System.Array * System.Array * int * int * System.Collections.IComparer -> unit
    static member Sort<'TKey,'TValue> : 'TKey [] * 'TValue [] * int * int * System.Collections.Generic.IComparer<'TKey> -> unit
    static member TrueForAll<'T> : 'T [] * System.Predicate<'T> -> bool
  end

Full name: System.Array

  type: Array
  implements: ICloneable
  implements: Collections.IList
  implements: Collections.ICollection
  implements: Collections.IEnumerable
val zeroCreate : int -> 'T []

Full name: Microsoft.FSharp.Collections.Array.zeroCreate
val download : (unit -> Async<unit>)
val count : int

  type: int
  implements: IComparable
  implements: IFormattable
  implements: IConvertible
  implements: IComparable<int>
  implements: IEquatable<int>
  inherits: ValueType
Multiple overloads
member Stream.AsyncRead : count:int -> Async<byte []>
member Stream.AsyncRead : buffer:byte [] * ?offset:int * ?count:int -> Async<int>
property Array.Length: int
member Stream.AsyncWrite : buffer:byte [] * ?offset:int * ?count:int -> Async<unit>
Stream.Seek(offset: int64, origin: SeekOrigin) : int64
type SeekOrigin =
  | Begin = 0
  | Current = 1
  | End = 2

Full name: System.IO.SeekOrigin

  type: SeekOrigin
  inherits: Enum
  inherits: ValueType
field SeekOrigin.Begin = 0
val ignore : 'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
val html : string

  type: string
  implements: IComparable
  implements: ICloneable
  implements: IConvertible
  implements: IComparable<string>
  implements: seq<char>
  implements: Collections.IEnumerable
  implements: IEquatable<string>
type StreamReader =
  class
    inherit System.IO.TextReader
    new : System.IO.Stream -> System.IO.StreamReader
    new : System.IO.Stream * bool -> System.IO.StreamReader
    new : System.IO.Stream * System.Text.Encoding -> System.IO.StreamReader
    new : System.IO.Stream * System.Text.Encoding * bool -> System.IO.StreamReader
    new : System.IO.Stream * System.Text.Encoding * bool * int -> System.IO.StreamReader
    new : string -> System.IO.StreamReader
    new : string * bool -> System.IO.StreamReader
    new : string * System.Text.Encoding -> System.IO.StreamReader
    new : string * System.Text.Encoding * bool -> System.IO.StreamReader
    new : string * System.Text.Encoding * bool * int -> System.IO.StreamReader
    member BaseStream : System.IO.Stream
    member Close : unit -> unit
    member CurrentEncoding : System.Text.Encoding
    member DiscardBufferedData : unit -> unit
    member EndOfStream : bool
    member Peek : unit -> int
    member Read : unit -> int
    member Read : char [] * int * int -> int
    member ReadLine : unit -> string
    member ReadToEnd : unit -> string
    static val Null : System.IO.StreamReader
  end

Full name: System.IO.StreamReader

  type: StreamReader
  implements: IDisposable
  inherits: TextReader
  inherits: MarshalByRefObject
Multiple overloads
Regex.Match(input: string) : Match
Regex.Match(input: string, startat: int) : Match
Regex.Match(input: string, beginning: int, length: int) : Match
property String.Length: int
val comparePages : Async<unit>

Full name: Demo.comparePages

Downloads pages in parallel and prints all results
val results : (string * int) []

  type: (string * int) []
  implements: ICloneable
  implements: Collections.IList
  implements: Collections.ICollection
  implements: Collections.Generic.IList<string * int>
  implements: Collections.Generic.ICollection<string * int>
  implements: seq<string * int>
  implements: Collections.IEnumerable
  inherits: Array
val map : ('T -> 'U) -> 'T [] -> 'U []

Full name: Microsoft.FSharp.Collections.Array.map
Multiple items
type Async<'T>

Full name: Microsoft.FSharp.Control.Async<_>

--------------------

type Async
with
  static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
  static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
  static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
  static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
  static member CancelDefaultToken : unit -> unit
  static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
  static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
  static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
  static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
  static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
  static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
  static member Ignore : computation:Async<'T> -> Async<unit>
  static member OnCancel : interruption:(unit -> unit) -> Async<IDisposable>
  static member Parallel : computations:seq<Async<'T>> -> Async<'T []>
  static member RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:CancellationToken -> 'T
  static member Sleep : millisecondsDueTime:int -> Async<unit>
  static member Start : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
  static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
  static member StartImmediate : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
  static member StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:CancellationToken -> unit
  static member SwitchToContext : syncContext:SynchronizationContext -> Async<unit>
  static member SwitchToNewThread : unit -> Async<unit>
  static member SwitchToThreadPool : unit -> Async<unit>
  static member TryCancelled : computation:Async<'T> * compensation:(OperationCanceledException -> unit) -> Async<'T>
  static member CancellationToken : Async<CancellationToken>
  static member DefaultCancellationToken : CancellationToken
end

Full name: Microsoft.FSharp.Control.Async
static member Async.Parallel : computations:seq<Async<'T>> -> Async<'T []>
val title : string

  type: string
  implements: IComparable
  implements: ICloneable
  implements: IConvertible
  implements: IComparable<string>
  implements: seq<char>
  implements: Collections.IEnumerable
  implements: IEquatable<string>
val length : int

  type: int
  implements: IComparable
  implements: IFormattable
  implements: IConvertible
  implements: IComparable<int>
  implements: IEquatable<int>
  inherits: ValueType
type Console =
  class
    static member BackgroundColor : System.ConsoleColor with get, set
    static member Beep : unit -> unit
    static member Beep : int * int -> unit
    static member BufferHeight : int with get, set
    static member BufferWidth : int with get, set
    static member CapsLock : bool
    static member Clear : unit -> unit
    static member CursorLeft : int with get, set
    static member CursorSize : int with get, set
    static member CursorTop : int with get, set
    static member CursorVisible : bool with get, set
    static member Error : System.IO.TextWriter
    static member ForegroundColor : System.ConsoleColor with get, set
    static member In : System.IO.TextReader
    static member InputEncoding : System.Text.Encoding with get, set
    static member KeyAvailable : bool
    static member LargestWindowHeight : int
    static member LargestWindowWidth : int
    static member MoveBufferArea : int * int * int * int * int * int -> unit
    static member MoveBufferArea : int * int * int * int * int * int * char * System.ConsoleColor * System.ConsoleColor -> unit
    static member NumberLock : bool
    static member OpenStandardError : unit -> System.IO.Stream
    static member OpenStandardError : int -> System.IO.Stream
    static member OpenStandardInput : unit -> System.IO.Stream
    static member OpenStandardInput : int -> System.IO.Stream
    static member OpenStandardOutput : unit -> System.IO.Stream
    static member OpenStandardOutput : int -> System.IO.Stream
    static member Out : System.IO.TextWriter
    static member OutputEncoding : System.Text.Encoding with get, set
    static member Read : unit -> int
    static member ReadKey : unit -> System.ConsoleKeyInfo
    static member ReadKey : bool -> System.ConsoleKeyInfo
    static member ReadLine : unit -> string
    static member ResetColor : unit -> unit
    static member SetBufferSize : int * int -> unit
    static member SetCursorPosition : int * int -> unit
    static member SetError : System.IO.TextWriter -> unit
    static member SetIn : System.IO.TextReader -> unit
    static member SetOut : System.IO.TextWriter -> unit
    static member SetWindowPosition : int * int -> unit
    static member SetWindowSize : int * int -> unit
    static member Title : string with get, set
    static member TreatControlCAsInput : bool with get, set
    static member WindowHeight : int with get, set
    static member WindowLeft : int with get, set
    static member WindowTop : int with get, set
    static member WindowWidth : int with get, set
    static member Write : bool -> unit
    static member Write : char -> unit
    static member Write : char [] -> unit
    static member Write : float -> unit
    static member Write : decimal -> unit
    static member Write : float32 -> unit
    static member Write : int -> unit
    static member Write : uint32 -> unit
    static member Write : int64 -> unit
    static member Write : uint64 -> unit
    static member Write : obj -> unit
    static member Write : string -> unit
    static member Write : string * obj -> unit
    static member Write : string * obj [] -> unit
    static member Write : string * obj * obj -> unit
    static member Write : char [] * int * int -> unit
    static member Write : string * obj * obj * obj -> unit
    static member Write : string * obj * obj * obj * obj -> unit
    static member WriteLine : unit -> unit
    static member WriteLine : bool -> unit
    static member WriteLine : char -> unit
    static member WriteLine : char [] -> unit
    static member WriteLine : decimal -> unit
    static member WriteLine : float -> unit
    static member WriteLine : float32 -> unit
    static member WriteLine : int -> unit
    static member WriteLine : uint32 -> unit
    static member WriteLine : int64 -> unit
    static member WriteLine : uint64 -> unit
    static member WriteLine : obj -> unit
    static member WriteLine : string -> unit
    static member WriteLine : string * obj -> unit
    static member WriteLine : string * obj [] -> unit
    static member WriteLine : char [] * int * int -> unit
    static member WriteLine : string * obj * obj -> unit
    static member WriteLine : string * obj * obj * obj -> unit
    static member WriteLine : string * obj * obj * obj * obj -> unit
  end

Full name: System.Console
Multiple overloads
Console.WriteLine() : unit
Console.WriteLine(value: string) : unit
Console.WriteLine(value: obj) : unit
Console.WriteLine(value: uint64) : unit
Console.WriteLine(value: int64) : unit
Console.WriteLine(value: uint32) : unit
Console.WriteLine(value: int) : unit
Console.WriteLine(value: float32) : unit
Console.WriteLine(value: float) : unit
Console.WriteLine(value: decimal) : unit
   (+9 other overloads)
static member Async.RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:CancellationToken -> 'T

Published: Friday, 29 October 2010, 4:34 AM
Author: Tomas Petricek
Typos: Send me a pull request!
Tags: c#, asynchronous, f#