3

我正在尝试通过几个示例来弄清楚 lwt 具体在做什么:

如果我有:

let%lwt x = f () in
let%lwt y = g () in
return ()

这是否运行 f 然后 g,或者由于 y 不依赖于 x,它会并行运行吗?

4

2 回答 2

4

该特定示例按顺序运行,即f ()直到解决返回的承诺之后才开始。g ()g ()f ()

看这个的方法是,当看

let%lwt x = e in
e'

意识到它e'成为回调的主体,只有在x可用时才会运行。因此,在问题的代码中,Lwt 首先看到:

(* Do this first. *)
let%lwt x = f () in

(* Do this once is available. *)
let%lwt y = g () in
return ()

并且,一旦x可用,它就会留下

(* Do this next. *)
let%lwt y = g () in

(* Do this once y is available. *)
return ()

为了避免这种序列化,调用f ()并且g ()首先在没有任何干预let%lwt的情况下将变量绑定到 Promise x'y'这些函数返回并等待 Promise:

let x' = f () in
let y' = g () in
let%lwt x = x' in
let%lwt y = y' in
return ()

(回答标题,Lwt 不使用数据依赖关系。我不认为库可以访问这种数据依赖关系信息)。

于 2019-09-22T13:14:57.907 回答
2

在您的代码中,不,因为您使用Lwt.t的是单子,而不是应用程序。

单子

您可能已经熟悉异步 IO 以及函数Lwt.bind : 'a Lwt.t -> ('a -> 'b Lwt.t) -> 'b Lwt.tLwt.return : 'a -> 'a Lwt.t. 不过,以防万一,我将简要回顾一下:

  • Lwt.bind promise callbackawaits promise,并在解决后,调用callback结果,取回另一个承诺。
  • Lwt.return data创建一个解析为data.

'a tmonad 是具有一些功能bind : 'a t -> ('a -> 'b t) -> 'b t和一些功能的泛型类型return : 'a -> 'a t。(这些函数也必须遵循一定的规律,但我离题了。)显然,类型'a Lwt.t与函数Lwt.bindLwt.return形成一个monad。

当人们想要表示某种“效果”或“计算”时,Monad 是一种常见的函数式编程模式,在这种情况下是异步 IO。Monad 很强大,因为该bind函数允许以后的计算依赖于早期计算的结果。Ifm : 'a t表示导致 的某些计算'a,并且f : 'a -> 'b t是一个使用 an'a执行导致 a 的计算的函数'b,则bind m fmakef取决于 的结果m

在 的情况下Lwt.bind promise callbackcallback取决于 的结果promise。中的代码在解决callback之前无法运行promise

当你写

let%lwt x = f () in
let%lwt y = g () in
return ()

你真的在写Lwt.bind (f ()) (fun x -> Lwt.bind (g ()) (fun y -> return ()))。因为g ()在回调内部,所以在解决之前不会运行f ()

应用程序

与 monad 相关的函数式编程模式是 applicative。应用程序是'a t具有函数map : ('a -> 'b) -> 'a t -> 'b t、函数return : 'a -> 'a t和函数的泛型类型both : 'a t * 'b t -> ('a * 'b) t。然而,与 monad 不同的是,应用程序不需要具有bind : 'a t -> ('a -> 'b t) -> 'b t,这意味着仅使用应用程序,以后的计算不能依赖于以前的计算。所有单子都是应用程序,但并非所有应用程序都是单子。

因为g ()不依赖于结果f (),你的代码可以重写使用both

let (let*) = bind
let (and*) = both

let* x = f ()
and* y = g () in
return ()

此代码转换为bind (fun (x, y) -> return ()) (both (f ()) (g ())). f ()g ()出现在回调之外bind,这意味着它们立即运行并且可以并行等待。both结合f ()g ()成为一个单一的承诺。

(let*)(and*)运算符是 OCaml 4.08 的新手。如果您使用的是早期版本的 OCaml,您可以直接编写翻译。

于 2019-09-23T23:23:23.987 回答