F#是否具有等同于OCaml的Async库的功能?具体来说,是否有能力轻松创建Deferred值并在填充后执行函数?
特别是,我要等到特定的引用发生更改,然后再执行某些操作。在OCaml中,我可以通过创建一个ivar并调用其read函数来实现。我该如何在F#中执行此操作?
最佳答案
简短答案
是的,我的Hopac库是Jane Street的Async库和F#的asynchronous workflows的近亲。 Hopac基于Concurrent ML(CML),并且可以说(从技术角度而不是观点而言)目前提供了比这两种方法更具表达性的编程模型。
更长的答案
简街的Async的Deferred与.Net的Task非常相似。两者本质上都是comonadic抽象,并在其顶部写有monadic层。
在Hopac中,相对于Deferred的最接近的实际上是Promise。但是,Hopac不会直接为Promises提供一元化层。当然,您可以轻松编写一个,但是我建议您坐一会儿。而是,Hopac提供了Job monad来对轻量级线程进行编码。这类似于how F#'s asynchronous workflows are defined,在我的主观意见中感觉更自然和easier to reason about。
Jane Street的Async的Ivar是Hopac的IVar的近亲。两者都来自同一血统。
使CML和Hopac比Jane Street的Async和F#异步工作流更具表现力的是,可以使用同步事件(CML)或alternatives(Hopac)的组合器。使用这些多种并发协议(protocol)可以封装为一流的,可扩展的(高阶)选择性操作。
CML和Hopac的同步channels支持简单的集合点,这意味着在通信发生时,通信的两端是一致的。反过来,这又具有一些有趣的应用程序,例如实现Multicast channels的能力,该功能支持垃圾回收的发布-订阅样式的通信,而无需显式取消订阅(与Rx相反IObservable-IObserver)。
具体答案
这是一个F#交互式 session ,该 session 首先定义一个IVar,然后启动并发Job,该Job读取IVar的值并打印一条消息。最后,将IVar写入一个值:
> let nameVar : IVar<string> = ivar () ;;
val nameVar : IVar<string>
> start (nameVar |>> fun who -> printfn "Hello, %s!" who) ;;
val it : unit = ()
> run (nameVar <-= "Vesa") ;;
val it : unit = ()
> Hello, Vesa!
为了产生此输出,使用Hopac.fsx脚本启动了F#Interactive。