Thursday, August 18, 2011

Getting Started with the F# PowerPack - Part 2

In part 1 of this series, I provided a few links and examples of how to get started with the modules and types found in the math directory of the F# PowerPack. In this post, I'll do something similar with the command-line argument parser, AsyncOperations, AsyncStreamReader, and AsyncWorker.


The F# PowerPack comes with a command-line parser that makes it easy to parse command-line options provided by a user.

The following links provide examples:

Example by Laurent Le Brun
Example by Robert Pickering
C# Port of Laurent Le Brun's Example by Steve Gilham


Within the AsyncOperations.fsi file you'll find type extensions for StreamReader (AsyncReadToEnd) and File (AsyncOpenText, AsyncOpenRead, AsyncOpenWrite, AsyncAppendText, and AsyncOpen).

The following example is a modified version of one of the PowerPack unit tests, which shows AsyncOpenWrite and AsyncOpenRead in use.
open System.IO

async {
    async {
        let buffer = "F# is fun!"B
        use! is = File.AsyncOpenWrite "test.txt"
        do! is.AsyncWrite(buffer, 0, buffer.Length) 
        printfn "File written" } 
    |> Async.RunSynchronously

    async { 
        let buffer = Array.zeroCreate<byte>(7)
        use! is = File.AsyncOpenRead "test.txt"
        let! count = is.AsyncRead(buffer, 0, 7)
        printfn "File read"
        return count, buffer }
    |> Async.RunSynchronously |> ignore
|> Async.Start


The AsyncStreamReader type provides a simple approach to reading streams asynchronously. Here's an example:
open System
open System.IO
open System.Text

let text = [ new String([|for i in 1..1022 do yield 'x'|])
             new String([|for i in 1..1022 do yield 'y'|])
             new String([|for i in 1..1022 do yield 'z'|]) ].ToString()  

use stream = new MemoryStream()
stream.Write(Encoding.UTF8.GetBytes(text), 0, text.Length)
stream.Position <- 0L
let reader = new AsyncStreamReader(stream, Encoding.UTF8)          
async { let rec readAllChars () =
            async {
                let! eof = reader.EndOfStream
                if not eof then
                    let! character = reader.Read()
                    do printf "%s" (character.ToString())
                    return! readAllChars ()
                    printfn "%sRead complete" Environment.NewLine           
        do! readAllChars ()
|> Async.Start
Another example of AsyncStreamReader can be found here (see the answer provided by Brian McNamara).


The AsyncWorker type provides a great way to define and launch a background worker. Don Syme shows how to create and use a similar type in his Async and Parallel Design Patterns in F#: Reporting Progress with Events (plus Twitter Sample) post.

Here's an example of the AsyncWorker type from the PowerPack.
open Microsoft.FSharp.Control

let rec job = async {   
        for i in 1 .. 20 do  
            printfn "doing some work"
            do! Async.Sleep 300
            worker.ReportProgress i
    and worker : AsyncWorker<_> = AsyncWorker(job)

worker.ProgressChanged.Add(fun jobNumber -> printfn "job %d completed" jobNumber) 
worker.Error.Add(fun err -> printfn "Error: %A" err.Message)
worker.Completed.Add(fun _ -> printfn "All jobs have completed")
worker.Canceled.Add(fun _ -> printfn "Jobs have been canceled")

worker.RunAsync() |> ignore
Wrapping Up:

You can find working examples of each of these on my GitHub Also, if you're interested in seeing more async examples, check out the series (C# Async Examples in F#) that Chris Marinos just started.

No comments:

Post a Comment