Programming with F# asynchronous sequences

In F#, we can represent asynchronous operations that do not block threads and eventually return a value of type 'T using asynchronous workflows Async<'T>. Asynchronous workflows can be easily constructed using the computation expression syntax async { ... } and there are also a few combi­nators that express more advanced composition (such as parallel composition or fork-join parallelism).

Sometimes, we need to use asynchronous operations that return more than just one value. For example, when downloading data from the internet, we would like to create an asynchronous sequence that returns the data in chunks as they become available.

One way to represent asynchronous operations that produce multiple values is to use the IObservable<'T> type from .NET. This isn't always the best option though. Observables implement push-based model, which means that chunks of data are generated as quickly as possible (until all chunks are emitted). What if we wanted to take the chunks one-by-one after the previous chunk is processed?

In this article, I describe asynchronous sequences. An asynchronous sequence is a simple, yet very powerful concept based on asynchronous workflows. It follows the same core model: results are generated on demand and asynchronously. Unlike asynchronous workflows, asynchronous sequences can be called (on demand) to generate multiple values until the end of the sequence is reached.

I first discussed asynchronous sequences with Don Syme, Dmitry Lomov and Brian McNamara in an email thread a long time ago. Thanks to Don for enthusiasm about the concept and for the first implementation of some of the combinators!

Introducing asynchronous sequences

Asynchronous sequences that emit values of type 'T are represented by a type AsyncSeq<'T>. The type is surprisignly simple. It follows the same pattern as the definition of standard F# list or a LazyList<'T> type from F# PowerPack:

1: /// Asynchronous computation that produces either end of a sequence
2: /// (Nil) or the next value together with the rest of the sequence.
3: type AsyncSeq<'T> = Async<AsyncSeqInner<'T>> 
4: and AsyncSeqInner<'T> =
5:   | Nil
6:   | Cons of 'T * AsyncSeq<'T>

An asynchronous sequence is an asynchronous computation. When started, it eventually produces a value of type AsyncSeqInner<'T>. The generated value is either Nil representing the end of the sequence or Cons carrying a value together with the tail of the sequence. The tail is again an asynchronous sequence - it can be started to get the next element of the sequence (if there are any) or it can be disposed if we don't need any further elements. To summarize - when programming with asynchronous sequences, the consumer is in control of the evaluation:

  • When using AsyncSeq<'T>, we can pull elements from the sequence (although the pull operation is asynchronous and may take some time before it completes). This approach is more suitable if the source that generates the values can be controlled - for example, we can ask a web crawler for a next bulk of pages or we can ask a source that reads file for the next 1kB of data.
  • On the other hand, sources that cannot be controlled (such as clicks in the user-interface or incomming messages) are better represented using IObservable<'T>.

There are some ways of converting between these two models, but we'll discuss that later. First, let's take a look at some examples. Creating values of type AsyncSeq<'T> directly would be a bit cumbersome. Fortunately, we can use the computation expression syntax...

Creating asynchronous sequences

The code to create asynchronous sequence is very similar to standard F# asynchronous workflows. The only difference is that asynchronous sequence can return multiple values using the yield keyword. The following snippet creates a simple asynchronous sequence. When started, it returns 1 after 1 second. When the returned tail is started, it returns 2 after 1 more second. Finally, starting the newly returned tail will return Nil immediately:

1: // When accessed, generates numbers 1 and 2. The number 
2: // is returned 1 second after value is requested.
3: let oneTwo = asyncSeq { 
4:   do! Async.Sleep(1000)
5:   yield 1
6:   do! Async.Sleep(1000)
7:   yield 2 }

The asyncSeq { .. } block can contain the usual syntax of asynchronous workflows. In the example above, we use do! together with the standard Async.Sleep member to pause the asynchronous sequence for 1 second.

This example shows that asynchronous sequences fit naturally extremely well with asynchronous workflows. This is possible because the right-hand-side of let! and do! is of type Async<'T> as opposed to AsyncSeq<'T> (which would be the case for a usual monad).

To can get a better understanding of what syntax is allowed inside the asyncSeq { .. } block, let's look at the type signature of the AsyncSeqBuilder computation builder. The listing omits some members (for exception handling and while loop), but it shows the essence of asynchronous sequences:

 1: type AsyncSeqBuilder =
 2:   // Waits for the result of a single asynchronous 
 3:   // operation and then continues generating the sequence
 4:   member Bind  : Async<'T> * ('T -> AsyncSeq<'U>) -> AsyncSeq<'U>
 5: 
 6:   // For every element of the input (asynchronous) sequence, 
 7:   // yield all elements generated by the body of the for loop
 8:   member For : AsyncSeq<'T> * ('T -> AsyncSeq<'TResult>) -> AsyncSeq<'TResult>
 9:   member For : seq<'T>      * ('T -> AsyncSeq<'TResult>) -> AsyncSeq<'TResult>
10: 
11:   // Yield single/zero elements and concatenation of sequences
12:   member Yield : 'T   -> AsyncSeq<'T>
13:   member Zero  : unit -> AsyncSeq<'T>
14:   member Combine : AsyncSeq<'T> * AsyncSeq<'T> -> AsyncSeq<'T>
15: 
16: type Microsoft.FSharp.Control.AsyncBuilder with
17:   // For every element of the input asynchronous sequence,
18:   // perform the specified asynchronous workflow
19:   member For : AsyncSeq<'T> * ('T -> Async<unit>) -> Async<unit>

As already mentioned, the Bind operation is unusual, because it takes Async<'T>. It creates an asynchronous sequence that starts the workflow, then calls the specified function and then continues yielding elements using the generated asynchronous sequence.

If we want to create an asynchronous sequence that uses all elements of another asynchronous sequence, we need to use the for syntax (you'll find some examples in the next section). This is similar to how usual sequence expressions work. When you want to return all elements of some other sequence, you can write for loop containing yield. Alternatively, you can also write yield!, which is also supported for asynchronous sequences. The definition uses overloading, so it allows iterating over both synchronous and asynchronous sequences.

The listing also adds an overloaded For member to the computation builder for standard F# asynchronous workflows (the AsyncBuilder type). This allows us to easily consume an asynchronous sequence (I'll explain how this work when we look at an example later).

Downloading web pages sequentially

To get started with asynchronous sequences, the following example creates a computation that downloads a list of web pages a and returns their URL together with the size of the downloaded HTML content. The web pages are not downloaded in parallel - we start downloading the next page only after the previous one was downloaded and consumed by the caller.

If the list contained millions of URLs and the caller only needed the first three, we wouldn't want to waste all our bandwidth. However, it may be a good idea to process a few pages in advance and store them in some buffer - this can be easily done and you can find examples in the source code at the end of this article.

 1: let urls = 
 2:   [ "http://bing.com"; "http://yahoo.com"; 
 3:     "http://google.com"; "http://msn.com" ]
 4: 
 5: // Asynchronous sequence that returns URLs and lengths
 6: // of the downloaded HTML. Web pages from a given list
 7: // are downloaded asynchronously in sequence.
 8: let pages = asyncSeq {
 9:   use wc = new WebClient()
10:   for url in urls do
11:     try
12:       let! html = wc.AsyncDownloadString(Uri(url))
13:       yield url, html.Length 
14:     with _ -> 
15:       yield url, -1 }    

The asyncSeq { .. } block iterates over a (synchronous) list of URLs using a for loop. The body of the loop is an asynchronous sequence. We use try .. with to handle exceptions that may occur when evaluating the computation. Inside the exception handler, we download a single page and return its URL and size. If an exception occurs, the URL is still returned, but with -1 as the size.

Now that we have an asynchronous sequence, let's look how to consume it. The elements of the sequence are evaluated asynchronously, which may take a long time, so we need to use async { .. } to avoid blocking threads (the type AsyncSeq<'T> does not implement the seq<'T> interface):

1: // Asynchronous workflow that prints results
2: async {
3:   for url, length in pages do
4:     printfn "%s (%d)" url length }
5: |> Async.Start

This snippet uses the overloaded for loop that is added to the standard F# asynchronous workflow (as discussed earlier). When evaluating the loop, the workflow calls the AsyncSeq<'T> value to get the first element and a tail. Then it evaluates the body of the for loop (which is itself asynchronous and may take some time). When the body completes, the for loop calls the AsyncSeq<'T> that was returned previously as a tail.

This means that the elements of asynchronous sequence are generated on-demand. As with standard F# async workflows, there is no implicit parallelism. Parallelising a program is a separate concern and it can be easily done using a combinator that creates an asynchronous sequence that evaluates certain number of elements in advance and keeps them in a buffer.

In the above example, the body simply prints the element generated by the sequence and then immediately asks for the next element, so there is no room (or need) for parallelism.

Processing asynchronous sequences

The previous section demonstrated how to create asynchronous sequences explicitly using the asyncSeq { .. } computation builder. This is similar to constructing ordinary sequences using the seq { .. } notation. Once we have an asynchronous sequence, we can process it using several useful higher-order functions that implement filtering, projection and other useful operation. Many of the functions from the Seq module can be provided for asynchronous sequences too.

The following example demonstrates several of combinators. It takes the pages asynchronous sequence from the previous section and gets URLs of pages that are larger than 50kB. Then it prints the URLs to the console:

1: // Print URL of pages that are smaller than 50k
2: let printPages =
3:   pages 
4:   |> AsyncSeq.filter (fun (_, len) -> len < 50000)
5:   |> AsyncSeq.map fst
6:   |> AsyncSeq.iter (printfn "%s")
7: 
8: printPages |> Async.Start

In this example, the functions used as arguments to filter, map and iter are synchronous functions that complete immediately. As you can see using the tool tips, the type of map and filter is what you would expect - they take AsyncSeq<'T> and return another AsyncSeq<'R>. The type of iter is more interesting. It returns Async<unti>, so we can execute the operation in an asynchronous workflow and wait until the iteration is completed (This is the same behavior as Seq.iter, but it differs from similar operation for IObservable<'T> - the Observable.add function returns immediately.)

The complete library (available at the end of the article) also provides asynchronous versions of most of the operations. For example, if the input asynchronous sequence contained just URLs, we would need to (asynchronously) download the web page and then test whether the length is less than 50kB. This could be done using filterAsync, which is similar to filter with the only difference that the predicate (given as the first argument) is asynchronous function.

Combinators such as filter and filterAsync are very easy to implement using the asyncSeq computation builder:

 1: /// Return elements for which the predicate returns true
 2: let filter f (input : AsyncSeq<'T>) = asyncSeq {
 3:   for v in input do
 4:     if f v then yield v }
 5: 
 6: /// Return elements for which the asynchronous predicate returns true
 7: let filterAsync f (input : AsyncSeq<'T>) = asyncSeq {
 8:   for v in input do
 9:     let! b = f v
10:     if b then yield v }

The two functions demonstrate a great aspect of the programming model based on asynchronous sequences. We can reuse many of the standard patterns that are useful when working with either normal sequence expressions or when using asynchronous workflows. The definition of filter is essentially the same as what you'd write when implementing Seq.filter.

In these examples, the asyncSeq { .. } block contains an asynchronous for loop that iterates over another asynchronous sequence. In the body, it yields it performs some operation. In the second case, the operation is performed asynchronously using let!. When it completes, the body (optionally) uses yield to generate the next value. When the consumer of the sequence returned by filter or filterAsync requests the next element, the sequence resumes, asks the input sequence for the next element and loops until it finds an element matching the predicate.

I will not discuss all combinators that are implemented in the AsyncSeq module. In general, the module implements most of the functionality that is available in the standard Seq module (and each function is well documented). However, the following listing shows a few declarations of interesting and useful functions:

 1: // Aggregate all elements of async sequence using async function 
 2: // and return the result (as an asynchronous workflow)
 3: val foldAsync : ('S -> 'T -> Async<'S>) -> 'S -> AsyncSeq<'T> -> Async<'S>
 4: 
 5: // Aggregate elements and yield immediate results as an async sequence
 6: // (The input is accessed lazily as the result sequence is evaluated)
 7: val scanAsync : ('S -> 'T -> Async<'S>) -> 'S -> AsyncSeq<'T> -> AsyncSeq<'S>
 8: 
 9: // Same as previous functions, but the aggregating function is synchronous
10: val fold : ('S -> 'T -> 'S) ->  'S -> AsyncSeq<'T> -> Async<'S>
11: val scan : ('S -> 'T -> 'S) ->  'S -> AsyncSeq<'T> -> AsyncSeq<'S>
12: 
13: // Synchronous and asynchronous version of a function that returns 
14: // elements of the input sequence (lazily) while a predicate holds
15: val takeWhileAsync : ('T -> Async<bool>) -> AsyncSeq<'T> -> AsyncSeq<'T>
16: val takeWhile : ('T -> bool) -> AsyncSeq<'T> -> AsyncSeq<'T>

Two very useful functions in functional programming are fold and scan. In our setting, fold aggregates an asynchronous sequence using the specified function and returns an asynchronous workflow that produces the aggregate when the asynchronous sequence ends. The scan function is similar, but it generates an asynchronous sequence of immediate results. As you can see, there are two versions of both functions: one that uses synchronous and one that uses asynchronous aggregation function.

Implementing sequential web crawler

Web crawlers are popular examples when demonstrating asynchronous programming in F#, so I'll use the same problem to implement a larger example based on asynchronous sequences. The usual web crawler examples in F# demonstrate that you can easily run a large number of asynchronous operations in parallel. This example focuses on something else:

  • We use asynchronous sequence to control the web crawler. The crawler continues processing only when the consumer is ready to handle the next value. This means that asynchronous sequences give the caller a way to control the source.
  • The code that consumes pages produced by the crawler uses just the AsyncSeq<'T> interface and it can use the functions designed for working with it without knowing how the crawler works. This means that asynchronous sequences are a useful abstraction representing asynchronously produced values.

The crawler in this simple exeample is fully sequential - it downloads a single page, passes it to the consumer and then waits until the consumer asks for the next page. We could improve the crawler by downloading several pages in advance (in parallel) and storing them in a buffer. You can find this feature in the complete source code at the end of the article. The important point is that the caller can still control the crawler and can still use the unified interface.

The following snippet implements a function randomCrawl that takes an initial URL and then continues crawling from this URL. It is implemented using a recursive loop function. If it reaches a page that was not visited before, it returns the URL together with an extracted title and then recursively visits all pages referenced from the current document. The code following yield will be executed only after the consumer processes the first value and asks for another:

 1: /// Crawl the internet starting from the specified page.
 2: /// From each page follow the first not-yet-visited page.
 3: let rec randomCrawl url = 
 4:   let visited = new System.Collections.Generic.HashSet<_>()
 5: 
 6:   // Visits page and then recursively visits all referenced pages
 7:   let rec loop url = asyncSeq {
 8:     if visited.Add(url) then
 9:       let! doc = downloadDocument url
10:       match doc with 
11:       | Some doc ->
12:           // Yield url and title as the next element
13:           yield url, getTitle doc
14:           // For every link, yield all referenced pages too
15:           for link in extractLinks doc do
16:             yield! loop link 
17:       | _ -> () }
18:   loop url

The main function in the snippet is written as an ordinary function. It initializes a HashMap<string> for keeping a set of visited URLs. There is no parallelism in the example, so we can use a collection that is not thread-safe. Next, the function declares an inner function loop that returns an asynchronous sequence and calls it with an initial URL as the argument.

Inside the loop function, we first test if the URL was already visited. If yes, the asynchronous sequence immediately ends (returning Nil). Otherwise, it asynchronously downloads the page and, if it succeeds, returns its URL and title. After yield, it iterates over all links and returns all pages (asynchronously) generated by recursive calls to loop.

Once we created randomCrawl, we can start using the crawler to implement some interesting functionality. For example, the following snippet takes first 10 pages that are referenced from Bing News and are on another domain:

1: // Use AsyncSeq combinators to print the titles of the first 10
2: // web sites that are from other domains than bing.com
3: randomCrawl "http://news.bing.com"
4: |> AsyncSeq.filter (fun (url, title) -> url.Contains("bing.com") |> not)
5: |> AsyncSeq.map snd
6: |> AsyncSeq.take 10
7: |> AsyncSeq.iter (printfn "%s")
8: |> Async.Start

Implementing the processing using AsyncSeq combinators is quite straightforward. At the end, we use AsyncSeq.iter to perform some action with the results and then start the returned asynchronous workflow using Async.Start.

It is worth repeating that the asynchronous sequence programming model allows us to control the crawler easily. When we use AsyncSeq.take to take the first 10 pages, it means that the crawler will stop after we consume the 10 pages. If we created the crawler differently (e.g. as an agent or an asynchronous workflow that writes to some thread-safe collection), we would have to program this functionality explicitly. This would be done using some control message (for agents), a global variable or a cancellation token. In any case, doing the same thing using asynchronous sequences is notably easier.

Exploring other applications

This article is just a brief introduction to programming with asynchronous sequences. I'll discuss some other interesting applications in the future and you can find several more complex examples in the complete source code. Before concluding, I'll briefly mention other possible applications and some functionality that the library provides.

One area where asynchronous sequences may be useful is when working with files. For example, let's say that we want to read all data from one stream and write them to another stream using a buffer of specified size. This can be implemented using a simple asynchronous workflow containing a loop. However, what if we wanted to split the two operations? Asynchronous sequences allow us to do exactly that using the following two extension members:

1: type System.IO.Stream with
2:   // Read the entire stream as an asynchronous 
3:   // sequence in chunks of the specified size
4:   member AsyncReadSeq : ?bufferSize:int -> AsyncSeq<byte []>
5: 
6: type System.IO.Stream with
7:   // Asynchronously write all data from an 
8:   // asynchronous sequence to the current stream.
9:   member AsyncWriteSeq : AsyncSeq<byte []> -> Async<unit>

The AsyncReadSeq member creates an asynchronou sequence that reads the content of a stream on-demand. The AsyncWriteSeq member creates an asynchronous workflow that reads all buffers from the input asynchronous sequence and writes them to the output stream. Together, the two operations behave exactly the same as asynchronous workflow containing a loop.

Note that this is a different behavior than what we could implement using IObservable<'T>. If the write operation takes longer than the read operation, the processing speed will be limited to the speed determined by the write operation. Reading more data than what can be actually written to the output would be unfortunate and we'd have to store it in some temporary buffer.

Another area where asynchronous sequences may fit quite nicely is the implementation of pipeline processing. In my earlier implementation [1] of pipeline processing in F#, I used a simple blocking queue implemented as an agent (see BlockingQueueAgent implementation on MSDN [3]). The queue is used between two processors and controls the process by blocking the producer (when the queue is full) or the consumer (when there are no values). This has some limitations - for example, there is no way to find out when the pipeline processing completes [2]. I believe that the same thing could be more elegantly implemented using asynchronous sequences, using some queueing combinator to connect the sequences.

Summary

In this article, we looked at asynchronous sequences, which is an abstraction that can be used for writing computations that generate multiple values asynchronously. Asynchronous sequences are based on standard F# asynchronous workflows and they work extremely well together. Asynchronous sequence can be constructed using a computation expression asyncSeq { .. } that can call asynchronous workflows. It can be also consumed using for loop inside asynchronous workflow.

The programming model behind asynchronous sequences is asynchronous pull. We can request the next element of the sequence and we eventually get a reply (which may be a value or Nil). This is different to the push model of IObservable<'T>, which generates values regardless of the consumer's state. Asynchronous sequences provide a good way to represent asynchronous data sources that can be controlled by the consumer, for example, when the consumer isn't ready to handle more data. Examples include web crawlers or reading records from a file or a database.

In this article, I demonstrated the computation builder for creating asynchronous sequences and some basic combinators for working with them. We've also seen a basic web crawler (and you can find a more advanced version in the samples below). There are quite a few interesting topics that didn't fit in this article and I hope to cover them in the future. For example, IObservable<'T> can be converted to an asynchronous sequence in two ways (AsyncSeq.ofObservable and AsyncSeq.ofObservableBuffered), they can be cached (AsyncSeq.cached) and they can be nicely used in conjunction with agents. You can learn more by exploring the additional samples and by writing some snippets and sharing them on fssnip.net!

Downloads & References 

Multiple items
module AsyncSeq

from FSharp.Control

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

type AsyncSeq<'T> = Async<AsyncSeqInner<'T>>

Full name: FSharp.Control.AsyncSeq<_>

Asynchronous computation that produces either end of a sequence
 (Nil) or the next value together with the rest of the sequence.

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 AwaitTask : task:Threading.Tasks.Task<'T> -> Async<'T>
  static member AwaitWaitHandle : waitHandle:Threading.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:Threading.CancellationToken -> 'T
  static member Sleep : millisecondsDueTime:int -> Async<unit>
  static member Start : computation:Async<unit> * ?cancellationToken:Threading.CancellationToken -> unit
  static member StartAsTask : computation:Async<'T> * ?taskCreationOptions:Threading.Tasks.TaskCreationOptions * ?cancellationToken:Threading.CancellationToken -> Threading.Tasks.Task<'T>
  static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
  static member StartChildAsTask : computation:Async<'T> * ?taskCreationOptions:Threading.Tasks.TaskCreationOptions -> Async<Threading.Tasks.Task<'T>>
  static member StartImmediate : computation:Async<unit> * ?cancellationToken:Threading.CancellationToken -> unit
  static member StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:Threading.CancellationToken -> unit
  static member SwitchToContext : syncContext:Threading.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<Threading.CancellationToken>
  static member DefaultCancellationToken : Threading.CancellationToken
end

Full name: Microsoft.FSharp.Control.Async
type AsyncSeqInner<'T> =
  | Nil
  | Cons of 'T * AsyncSeq<'T>

Full name: FSharp.Control.AsyncSeqInner<_>
union case AsyncSeqInner.Nil: AsyncSeqInner<'T>
union case AsyncSeqInner.Cons: 'T * AsyncSeq<'T> -> AsyncSeqInner<'T>
val oneTwo : AsyncSeq<int>

Full name: FSharp.Samples.oneTwo
val asyncSeq : AsyncSeq.AsyncSeqBuilder

Full name: FSharp.Control.AsyncSeqExtensions.asyncSeq
static member Async.Sleep : millisecondsDueTime:int -> Async<unit>
val urls : string list

Full name: FSharp.Samples.urls

  type: string list
  implements: Collections.IStructuralEquatable
  implements: IComparable<List<string>>
  implements: IComparable
  implements: Collections.IStructuralComparable
  implements: Collections.Generic.IEnumerable<string>
  implements: Collections.IEnumerable
val pages : AsyncSeq<string * int>

Full name: FSharp.Samples.pages
val wc : WebClient

  type: WebClient
  implements: ComponentModel.IComponent
  implements: IDisposable
  inherits: ComponentModel.Component
  inherits: MarshalByRefObject
type WebClient =
  class
    inherit System.ComponentModel.Component
    new : unit -> System.Net.WebClient
    member BaseAddress : string with get, set
    member CachePolicy : System.Net.Cache.RequestCachePolicy with get, set
    member CancelAsync : unit -> unit
    member Credentials : System.Net.ICredentials with get, set
    member DownloadData : string -> System.Byte []
    member DownloadData : System.Uri -> System.Byte []
    member DownloadDataAsync : System.Uri -> unit
    member DownloadDataAsync : System.Uri * obj -> unit
    member DownloadFile : string * string -> unit
    member DownloadFile : System.Uri * string -> unit
    member DownloadFileAsync : System.Uri * string -> unit
    member DownloadFileAsync : System.Uri * string * obj -> unit
    member DownloadString : string -> string
    member DownloadString : System.Uri -> string
    member DownloadStringAsync : System.Uri -> unit
    member DownloadStringAsync : System.Uri * obj -> unit
    member Encoding : System.Text.Encoding with get, set
    member Headers : System.Net.WebHeaderCollection with get, set
    member IsBusy : bool
    member OpenRead : string -> System.IO.Stream
    member OpenRead : System.Uri -> System.IO.Stream
    member OpenReadAsync : System.Uri -> unit
    member OpenReadAsync : System.Uri * obj -> unit
    member OpenWrite : string -> System.IO.Stream
    member OpenWrite : System.Uri -> System.IO.Stream
    member OpenWrite : string * string -> System.IO.Stream
    member OpenWrite : System.Uri * string -> System.IO.Stream
    member OpenWriteAsync : System.Uri -> unit
    member OpenWriteAsync : System.Uri * string -> unit
    member OpenWriteAsync : System.Uri * string * obj -> unit
    member Proxy : System.Net.IWebProxy with get, set
    member QueryString : System.Collections.Specialized.NameValueCollection with get, set
    member ResponseHeaders : System.Net.WebHeaderCollection
    member UploadData : string * System.Byte [] -> System.Byte []
    member UploadData : System.Uri * System.Byte [] -> System.Byte []
    member UploadData : string * string * System.Byte [] -> System.Byte []
    member UploadData : System.Uri * string * System.Byte [] -> System.Byte []
    member UploadDataAsync : System.Uri * System.Byte [] -> unit
    member UploadDataAsync : System.Uri * string * System.Byte [] -> unit
    member UploadDataAsync : System.Uri * string * System.Byte [] * obj -> unit
    member UploadFile : string * string -> System.Byte []
    member UploadFile : System.Uri * string -> System.Byte []
    member UploadFile : string * string * string -> System.Byte []
    member UploadFile : System.Uri * string * string -> System.Byte []
    member UploadFileAsync : System.Uri * string -> unit
    member UploadFileAsync : System.Uri * string * string -> unit
    member UploadFileAsync : System.Uri * string * string * obj -> unit
    member UploadString : string * string -> string
    member UploadString : System.Uri * string -> string
    member UploadString : string * string * string -> string
    member UploadString : System.Uri * string * string -> string
    member UploadStringAsync : System.Uri * string -> unit
    member UploadStringAsync : System.Uri * string * string -> unit
    member UploadStringAsync : System.Uri * string * string * obj -> unit
    member UploadValues : string * System.Collections.Specialized.NameValueCollection -> System.Byte []
    member UploadValues : System.Uri * System.Collections.Specialized.NameValueCollection -> System.Byte []
    member UploadValues : string * string * System.Collections.Specialized.NameValueCollection -> System.Byte []
    member UploadValues : System.Uri * string * System.Collections.Specialized.NameValueCollection -> System.Byte []
    member UploadValuesAsync : System.Uri * System.Collections.Specialized.NameValueCollection -> unit
    member UploadValuesAsync : System.Uri * string * System.Collections.Specialized.NameValueCollection -> unit
    member UploadValuesAsync : System.Uri * string * System.Collections.Specialized.NameValueCollection * obj -> unit
    member UseDefaultCredentials : bool with get, set
  end

Full name: System.Net.WebClient

  type: WebClient
  implements: ComponentModel.IComponent
  implements: IDisposable
  inherits: ComponentModel.Component
  inherits: MarshalByRefObject
val url : string

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

  type: string
  implements: IComparable
  implements: ICloneable
  implements: IConvertible
  implements: IComparable<string>
  implements: seq<char>
  implements: Collections.IEnumerable
  implements: IEquatable<string>
member WebClient.AsyncDownloadString : address:Uri -> Async<string>
type Uri =
  class
    new : string -> System.Uri
    new : string * bool -> System.Uri
    new : string * System.UriKind -> System.Uri
    new : System.Uri * string -> System.Uri
    new : System.Uri * string * bool -> System.Uri
    new : System.Uri * System.Uri -> System.Uri
    member AbsolutePath : string
    member AbsoluteUri : string
    member Authority : string
    member DnsSafeHost : string
    member Equals : obj -> bool
    member Fragment : string
    member GetComponents : System.UriComponents * System.UriFormat -> string
    member GetHashCode : unit -> int
    member GetLeftPart : System.UriPartial -> string
    member Host : string
    member HostNameType : System.UriHostNameType
    member IsAbsoluteUri : bool
    member IsBaseOf : System.Uri -> bool
    member IsDefaultPort : bool
    member IsFile : bool
    member IsLoopback : bool
    member IsUnc : bool
    member IsWellFormedOriginalString : unit -> bool
    member LocalPath : string
    member MakeRelative : System.Uri -> string
    member MakeRelativeUri : System.Uri -> System.Uri
    member OriginalString : string
    member PathAndQuery : string
    member Port : int
    member Query : string
    member Scheme : string
    member Segments : string []
    member ToString : unit -> string
    member UserEscaped : bool
    member UserInfo : string
    static val UriSchemeFile : string
    static val UriSchemeFtp : string
    static val UriSchemeGopher : string
    static val UriSchemeHttp : string
    static val UriSchemeHttps : string
    static val UriSchemeMailto : string
    static val UriSchemeNews : string
    static val UriSchemeNntp : string
    static val UriSchemeNetTcp : string
    static val UriSchemeNetPipe : string
    static val SchemeDelimiter : string
    static member CheckHostName : string -> System.UriHostNameType
    static member CheckSchemeName : string -> bool
    static member Compare : System.Uri * System.Uri * System.UriComponents * System.UriFormat * System.StringComparison -> int
    static member EscapeDataString : string -> string
    static member EscapeUriString : string -> string
    static member FromHex : char -> int
    static member HexEscape : char -> string
    static member HexUnescape : string * int -> char
    static member IsHexDigit : char -> bool
    static member IsHexEncoding : string * int -> bool
    static member IsWellFormedUriString : string * System.UriKind -> bool
    static member TryCreate : string * System.UriKind * System.Uri -> bool
    static member TryCreate : System.Uri * string * System.Uri -> bool
    static member TryCreate : System.Uri * System.Uri * System.Uri -> bool
    static member UnescapeDataString : string -> string
  end

Full name: System.Uri

  type: Uri
  implements: Runtime.Serialization.ISerializable
val async : AsyncBuilder

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
val length : int

  type: int
  implements: IComparable
  implements: IFormattable
  implements: IConvertible
  implements: IComparable<int>
  implements: IEquatable<int>
  inherits: ValueType
val printfn : Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
static member Async.Start : computation:Async<unit> * ?cancellationToken:Threading.CancellationToken -> unit
val printPages : Async<unit>

Full name: FSharp.Samples.printPages
Multiple items
module AsyncSeq

from FSharp.Control

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

type AsyncSeq<'T> = Async<AsyncSeqInner<'T>>

Full name: FSharp.Control.AsyncSeq<_>
val filter : ('T -> bool) -> AsyncSeq<'T> -> AsyncSeq<'T>

Full name: FSharp.Control.AsyncSeq.filter
val len : int

  type: int
  implements: IComparable
  implements: IFormattable
  implements: IConvertible
  implements: IComparable<int>
  implements: IEquatable<int>
  inherits: ValueType
val map : ('T -> 'a) -> AsyncSeq<'T> -> AsyncSeq<'a>

Full name: FSharp.Control.AsyncSeq.map
val fst : ('T1 * 'T2) -> 'T1

Full name: Microsoft.FSharp.Core.Operators.fst
val iter : ('T -> unit) -> AsyncSeq<'T> -> Async<unit>

Full name: FSharp.Control.AsyncSeq.iter
val filter : ('T -> bool) -> AsyncSeq<'T> -> AsyncSeq<'T>

Full name: FSharp.Samples.filter

Return elements for which the predicate returns true
val f : ('T -> bool)
val input : AsyncSeq<'T>
val v : 'T
val filterAsync : ('T -> Async<bool>) -> AsyncSeq<'T> -> AsyncSeq<'T>

Full name: FSharp.Samples.filterAsync

Return elements for which the asynchronous predicate returns true
val f : ('T -> Async<bool>)
val b : bool

  type: bool
  implements: IComparable
  implements: IConvertible
  implements: IComparable<bool>
  implements: IEquatable<bool>
  inherits: ValueType
val randomCrawl : string -> AsyncSeq<string * string>

Full name: FSharp.Crawler.randomCrawl

Crawl the internet starting from the specified page.
 From each page follow the first not-yet-visited page.

val visited : Collections.Generic.HashSet<string>

  type: Collections.Generic.HashSet<string>
  implements: Runtime.Serialization.ISerializable
  implements: Runtime.Serialization.IDeserializationCallback
  implements: Collections.Generic.ISet<string>
  implements: Collections.Generic.ICollection<string>
  implements: seq<string>
  implements: Collections.IEnumerable
namespace System
namespace System.Collections
namespace System.Collections.Generic
type HashSet<'T> =
  class
    new : unit -> System.Collections.Generic.HashSet<'T>
    new : System.Collections.Generic.IEqualityComparer<'T> -> System.Collections.Generic.HashSet<'T>
    new : System.Collections.Generic.IEnumerable<'T> -> System.Collections.Generic.HashSet<'T>
    new : System.Collections.Generic.IEnumerable<'T> * System.Collections.Generic.IEqualityComparer<'T> -> System.Collections.Generic.HashSet<'T>
    member Add : 'T -> bool
    member Clear : unit -> unit
    member Comparer : System.Collections.Generic.IEqualityComparer<'T>
    member Contains : 'T -> bool
    member CopyTo : 'T [] -> unit
    member CopyTo : 'T [] * int -> unit
    member CopyTo : 'T [] * int * int -> unit
    member Count : int
    member ExceptWith : System.Collections.Generic.IEnumerable<'T> -> unit
    member GetEnumerator : unit -> Enumerator<'T>
    member GetObjectData : System.Runtime.Serialization.SerializationInfo * System.Runtime.Serialization.StreamingContext -> unit
    member IntersectWith : System.Collections.Generic.IEnumerable<'T> -> unit
    member IsProperSubsetOf : System.Collections.Generic.IEnumerable<'T> -> bool
    member IsProperSupersetOf : System.Collections.Generic.IEnumerable<'T> -> bool
    member IsSubsetOf : System.Collections.Generic.IEnumerable<'T> -> bool
    member IsSupersetOf : System.Collections.Generic.IEnumerable<'T> -> bool
    member OnDeserialization : obj -> unit
    member Overlaps : System.Collections.Generic.IEnumerable<'T> -> bool
    member Remove : 'T -> bool
    member RemoveWhere : System.Predicate<'T> -> int
    member SetEquals : System.Collections.Generic.IEnumerable<'T> -> bool
    member SymmetricExceptWith : System.Collections.Generic.IEnumerable<'T> -> unit
    member TrimExcess : unit -> unit
    member UnionWith : System.Collections.Generic.IEnumerable<'T> -> unit
    static member CreateSetComparer : unit -> System.Collections.Generic.IEqualityComparer<System.Collections.Generic.HashSet<'T>>
    type Enumerator =
      struct
        member Current : 'T
        member Dispose : unit -> unit
        member MoveNext : unit -> bool
      end
  end

Full name: System.Collections.Generic.HashSet<_>

  type: Collections.Generic.HashSet<'T>
  implements: Runtime.Serialization.ISerializable
  implements: Runtime.Serialization.IDeserializationCallback
  implements: Collections.Generic.ISet<'T>
  implements: Collections.Generic.ICollection<'T>
  implements: seq<'T>
  implements: Collections.IEnumerable
val loop : (string -> AsyncSeq<string * string>)
Collections.Generic.HashSet.Add(item: string) : bool
val doc : HtmlDocument option

  type: HtmlDocument option
  implements: Collections.IStructuralEquatable
  implements: IComparable<Option<HtmlDocument>>
  implements: IComparable
  implements: Collections.IStructuralComparable
val downloadDocument : string -> Async<HtmlDocument option>

Full name: FSharp.Crawler.downloadDocument

Asynchronously download the document and parse the HTML
union case Option.Some: 'T -> Option<'T>
val doc : HtmlDocument

  type: HtmlDocument
  implements: Xml.XPath.IXPathNavigable
val getTitle : HtmlDocument -> string

Full name: FSharp.Crawler.getTitle

Extract the <title> of the web page
val link : string

  type: string
  implements: IComparable
  implements: ICloneable
  implements: IConvertible
  implements: IComparable<string>
  implements: seq<char>
  implements: Collections.IEnumerable
  implements: IEquatable<string>
val extractLinks : HtmlDocument -> string list

Full name: FSharp.Crawler.extractLinks

Extract all links from the document that start with "http://"
val title : string

  type: string
  implements: IComparable
  implements: ICloneable
  implements: IConvertible
  implements: IComparable<string>
  implements: seq<char>
  implements: Collections.IEnumerable
  implements: IEquatable<string>
val not : bool -> bool

Full name: Microsoft.FSharp.Core.Operators.not
val snd : ('T1 * 'T2) -> 'T2

Full name: Microsoft.FSharp.Core.Operators.snd
val take : int -> AsyncSeq<'T> -> AsyncSeq<'T>

Full name: FSharp.Control.AsyncSeq.take
type AsyncSeqBuilder =
  class
    member Bind : Async<'T> * ('T -> AsyncSeq<'U>) -> AsyncSeq<'U>
    member Combine : AsyncSeq<'T> * AsyncSeq<'T> -> AsyncSeq<'T>
    member For : AsyncSeq<'T> * ('T -> AsyncSeq<'TResult>) -> AsyncSeq<'TResult>
    member For : seq<'T> * ('T -> AsyncSeq<'TResult>) -> AsyncSeq<'TResult>
    member Yield : 'T -> AsyncSeq<'T>
    member Zero : unit -> AsyncSeq<'T>
  end

Full name: FSharp.Control.AsyncSeq.AsyncSeqBuilder
member AsyncSeqBuilder.Bind : Async<'T> * ('T -> AsyncSeq<'U>) -> AsyncSeq<'U>

Full name: FSharp.Control.AsyncSeq.AsyncSeqBuilder.Bind
Multiple items
type Async<'T>

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

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

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

Full name: Microsoft.FSharp.Control.Async
type AsyncSeq<'T> = Async<AsyncSeqInner<'T>>

Full name: FSharp.Control.AsyncSeq<_>
member AsyncSeqBuilder.For : AsyncSeq<'T> * ('T -> AsyncSeq<'TResult>) -> AsyncSeq<'TResult>

Full name: FSharp.Control.AsyncSeq.AsyncSeqBuilder.For
member AsyncSeqBuilder.For : seq<'T> * ('T -> AsyncSeq<'TResult>) -> AsyncSeq<'TResult>

Full name: FSharp.Control.AsyncSeq.AsyncSeqBuilder.For
Multiple items
val seq : seq<'T> -> seq<'T>

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

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

type seq<'T> = System.Collections.Generic.IEnumerable<'T>

Full name: Microsoft.FSharp.Collections.seq<_>

  type: seq<'T>
  inherits: System.Collections.IEnumerable
member AsyncSeqBuilder.Yield : 'T -> AsyncSeq<'T>

Full name: FSharp.Control.AsyncSeq.AsyncSeqBuilder.Yield
member AsyncSeqBuilder.Zero : unit -> AsyncSeq<'T>

Full name: FSharp.Control.AsyncSeq.AsyncSeqBuilder.Zero
type unit = Unit

Full name: Microsoft.FSharp.Core.unit

  type: unit
  implements: System.IComparable
member AsyncSeqBuilder.Combine : AsyncSeq<'T> * AsyncSeq<'T> -> AsyncSeq<'T>

Full name: FSharp.Control.AsyncSeq.AsyncSeqBuilder.Combine
namespace Microsoft
namespace Microsoft.FSharp
namespace Microsoft.FSharp.Control
type AsyncBuilder =
  class
    private new : unit -> AsyncBuilder
    member Bind : computation:Async<'T> * binder:('T -> Async<'U>) -> Async<'U>
    member Combine : computation1:Async<unit> * computation2:Async<'T> -> Async<'T>
    member Delay : generator:(unit -> Async<'T>) -> Async<'T>
    member For : sequence:seq<'T> * body:('T -> Async<unit>) -> Async<unit>
    member Return : value:'T -> Async<'T>
    member ReturnFrom : computation:Async<'T> -> Async<'T>
    member TryFinally : computation:Async<'T> * compensation:(unit -> unit) -> Async<'T>
    member TryWith : computation:Async<'T> * catchHandler:(exn -> Async<'T>) -> Async<'T>
    member Using : resource:'T * binder:('T -> Async<'U>) -> Async<'U> (requires 'T :> System.IDisposable)
    member While : guard:(unit -> bool) * computation:Async<unit> -> Async<unit>
    member Zero : unit -> Async<unit>
  end

Full name: Microsoft.FSharp.Control.AsyncBuilder
member AsyncBuilder.For : AsyncSeq<'T> * ('T -> Async<unit>) -> Async<unit>

Full name: FSharp.Control.AsyncSeq.For
val foldAsync : ('S -> 'T -> Async<'S>) -> 'S -> AsyncSeq<'T> -> Async<'S>

Full name: FSharp.Control.AsyncSeq.foldAsync
val scanAsync : ('S -> 'T -> Async<'S>) -> 'S -> AsyncSeq<'T> -> AsyncSeq<'S>

Full name: FSharp.Control.AsyncSeq.scanAsync
val fold : ('S -> 'T -> 'S) -> 'S -> AsyncSeq<'T> -> Async<'S>

Full name: FSharp.Control.AsyncSeq.fold
val scan : ('S -> 'T -> 'S) -> 'S -> AsyncSeq<'T> -> AsyncSeq<'S>

Full name: FSharp.Control.AsyncSeq.scan
val takeWhileAsync : ('T -> Async<bool>) -> AsyncSeq<'T> -> AsyncSeq<'T>

Full name: FSharp.Control.AsyncSeq.takeWhileAsync
type bool = System.Boolean

Full name: Microsoft.FSharp.Core.bool

  type: bool
  implements: System.IComparable
  implements: System.IConvertible
  implements: System.IComparable<bool>
  implements: System.IEquatable<bool>
  inherits: System.ValueType
val takeWhile : ('T -> bool) -> AsyncSeq<'T> -> AsyncSeq<'T>

Full name: FSharp.Control.AsyncSeq.takeWhile
namespace System
namespace System.IO
type Stream =
  class
    inherit System.MarshalByRefObject
    member BeginRead : System.Byte [] * int * int * System.AsyncCallback * obj -> System.IAsyncResult
    member BeginWrite : System.Byte [] * int * int * System.AsyncCallback * obj -> System.IAsyncResult
    member CanRead : bool
    member CanSeek : bool
    member CanTimeout : bool
    member CanWrite : bool
    member Close : unit -> unit
    member CopyTo : System.IO.Stream -> unit
    member CopyTo : System.IO.Stream * int -> unit
    member Dispose : unit -> unit
    member EndRead : System.IAsyncResult -> int
    member EndWrite : System.IAsyncResult -> unit
    member Flush : unit -> unit
    member Length : int64
    member Position : int64 with get, set
    member Read : System.Byte [] * int * int -> int
    member ReadByte : unit -> int
    member ReadTimeout : int with get, set
    member Seek : int64 * System.IO.SeekOrigin -> int64
    member SetLength : int64 -> unit
    member Write : System.Byte [] * int * int -> unit
    member WriteByte : System.Byte -> unit
    member WriteTimeout : int with get, set
    static val Null : System.IO.Stream
    static member Synchronized : System.IO.Stream -> System.IO.Stream
  end

Full name: System.IO.Stream

  type: System.IO.Stream
  implements: System.IDisposable
  inherits: System.MarshalByRefObject
member System.IO.Stream.AsyncReadSeq : ?bufferSize:int -> AsyncSeq<byte []>

Full name: FSharp.Control.IOExtensions.AsyncReadSeq
Multiple items
val int : 'T -> int (requires member op_Explicit)

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

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

type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>

  type: int<'Measure>
  implements: System.IComparable
  implements: System.IConvertible
  implements: System.IFormattable
  implements: System.IComparable<int<'Measure>>
  implements: System.IEquatable<int<'Measure>>
  inherits: System.ValueType


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

type int = int32

Full name: Microsoft.FSharp.Core.int

  type: int
  implements: System.IComparable
  implements: System.IFormattable
  implements: System.IConvertible
  implements: System.IComparable<int>
  implements: System.IEquatable<int>
  inherits: System.ValueType
Multiple items
module AsyncSeq

from FSharp.Control

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

type AsyncSeq<'T> = Async<AsyncSeqInner<'T>>

Full name: FSharp.Control.AsyncSeq<_>
Multiple items
val byte : 'T -> byte (requires member op_Explicit)

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

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

type byte = System.Byte

Full name: Microsoft.FSharp.Core.byte

  type: byte
  implements: System.IComparable
  implements: System.IFormattable
  implements: System.IConvertible
  implements: System.IComparable<byte>
  implements: System.IEquatable<byte>
  inherits: System.ValueType
member System.IO.Stream.AsyncWriteSeq : AsyncSeq<byte []> -> Async<unit>

Full name: FSharp.Control.IOExtensions.AsyncWriteSeq

Discuss on twitter, .
Send corrections via GitHub pull requests.