该表达式预计具有类型' unit'但这里有类型' unit []' for Async.Parallel |> Async.RunSynchronously

时间:2018-06-07 22:21:06

标签: f#

我在F#控制台应用程序的main函数中有以下代码。

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

然后我尝试并行运行这些项目。

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

它出现了错误

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

添加|> ignore会导致F#无法运行Seq.map()中的代码。

1 个答案:

答案 0 :(得分:3)

我故意不回答你的问题。相反,如果您偶尔偶然发现异步工作流程,我将分享您可能用于救援的方法。诀窍是完全接受F#推广的探索性编程风格。

让我们从最小的异步工作流程开始,可能是async { () }。在FSI评估

let dummywf = async { () }

产量

val dummywf : Async<unit>

显示dummywf值确实是异步工作流程(虽然是非虚拟的)。

异步工作流程可以组成序列,最简单的一个可能是列表[ dummywf ]。评估它会显示Async<unit> list类型,它代表了所寻求的类型。

现在,让我们回想一下Async.Parallel method的签名,即seq<Async<'T>> -> Async<'T[]>,即它需要一系列异步计算并将其转换为异步计算,最终通过异步计算其成员来评估产生一个值'T的数组。检查FSI,表达式

[ dummywf ] |> Async.Parallel

确实评估为Async<unit []>类型的值。

最后,让我们通过使用最简单的Async.RunSynchronously<'T> method 形式将其组合成一个Async&lt;'T&gt;来实现这个值。 (在我们的示例中为类型Async<unit []>的值)并返回类型'T的评估结果(在我们的示例中为unit []。评估组合表达式

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

按预期收取[|()|]

在清楚地理解了上述构图之后,表现得如预期的那样,但是为了证明我们真的构建了一个完整的异步工作流程,让我们尝试一些简单但更令人印象深刻的东西。让我们实现一个int -> Async<unit>类型的函数,它接受int参数,进行非阻塞暂停,返回相同的unit,并且副作用打印参数的值:

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

现在让我们使用wasteSomeTime组成一系列具有不同参数的异步计算,并在类似于前一种情况的组合中对其进行评估:

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

输出类似于

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

确实说明了异步执行。

在浏览了上面的小型探索课程后,我很肯定你能够自己回答原来的问题。