Reputation: 29159
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
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