ca9163d9
ca9163d9

Reputation: 29159

This expression was expected to have type 'unit' but here has type 'unit []' for Async.Parallel |> Async.RunSynchronously

I have the following code in the main function of an F# console application.

let main ... = 
    let list = // get a list
    list 
    |> Seq.iter(fun i -> ....)
    ()

Then I tried to run the items in parallel.

let list = // get a list
list 
|> Seq.map(fun i -> async { .... })
|> Seq.toArray
|> Async.Parallel
|> Async.RunSynchronously // ERROR
// |> ignore
()

And it got the error of

Error   FS0001  This expression was expected to have type
    'unit'    
but here has type
    'unit []'   

Adding |> ignore will cause F# not to run the code in the Seq.map().

Upvotes: 1

Views: 539

Answers (1)

Gene Belitski
Gene Belitski

Reputation: 10350

I intentionally will not answer your question per se. Instead, I'll share an approach that you may use to the rescue if you occasionally stumble over asynchronous workflows. The trick is to fully embrace the exploratory programming style that F# promotes.

Let's begin with a minimal async workflow, which may be async { () }. Evaluating in FSI

let dummywf = async { () }

yields

val dummywf : Async<unit>

showing that dummywf value is an async workflow, indeed (although a really dummy one).

Async workflows may be composed into sequences, one of simplest may be the list [ dummywf ]. Evaluating it reveals the type Async<unit> list, which represents the sought-for one.

Now, let's recall the signature of Async.Parallel method, which is seq<Async<'T>> -> Async<'T[]>, i.e. it takes a sequence of async computations and turns it into an async computation that, being eventually evaluated via asynchronously evaluating its members will yield an array of values of 'T. Checking in FSI, the expression

[ dummywf ] |> Async.Parallel

evaluates into a value of type Async<unit []>, indeed.

Finally, let's materialize this value by composing it with the simplest form of Async.RunSynchronously<'T> method taking an Async<'T> (in our case a value of typeAsync<unit []>) and returning the result of evaluation of type 'T (in our case unit []. Evaluating the combined expression

[ dummywf ] |> Async.Parallel |> Async.RunSynchronously

yields [|()|] as expected.

After getting a crystal clear understanding of the above composition that behaves as expected, but is a way too simple for demonstrating that we really built a full-blown async workflow, let's try something still simple, but more impressive. Let's implement a function of type int -> Async<unit> that takes an int argument, makes a non-blocking pause, returns the same unit and as a side effect prints the value of the argument:

let wasteSomeTime x =
    async {
        do! Async.Sleep(100)
        printfn "%d" x
    }

Now let's use wasteSomeTime to compose a sequence of async computations with distinct arguments and evaluate it within a composition similar to the one in the previous case:

let wasteful = [1..10] |> Seq.map wasteSomeTime // binds to seq<Async<unit>>
wasteful |> Async.Parallel |> Async.RunSynchronously |> ignore    

The output will be resembling

> 
4
2
3
6
8
10
7
5
9
1
val it : unit = ()

that makes evident the asynchronous execution, indeed.

After grokking the bits and pieces of the small exploratory session above I'm positive you'll be able to answer your original question by yourself.

Upvotes: 2

Related Questions