I'm trying to work out what specifically lwt is doing in a couple of examples:
If I have:
let%lwt x = f () in
let%lwt y = g () in
return ()
Does this run f then g, or since y doesn't rely on x will it run both in parallel?
On
That particular example runs f () and g () in sequence, i.e. g () doesn't start until after the promise returned by f () is resolved.
The way to see this is, when looking at
let%lwt x = e in
e'
to realize that e' becomes the body of a callback, that will run only when x is available. So, in the code in the question, Lwt first sees:
(* Do this first. *)
let%lwt x = f () in
(* Do this once is available. *)
let%lwt y = g () in
return ()
and, once x is available, it is left with
(* Do this next. *)
let%lwt y = g () in
(* Do this once y is available. *)
return ()
To avoid this serialization, call f () and g () first, without any intervening let%lwt, bind variables to the promises x', y' these functions return, and wait on the promises:
let x' = f () in
let y' = g () in
let%lwt x = x' in
let%lwt y = y' in
return ()
(And to answer the title, Lwt does not use data dependencies. I don't think a library could have access to this kind of data dependency information).
In your code, no, because you are using
Lwt.tas monad, rather than as an applicative.Monads
You're probably already familiar with asynchronous IO and the functions
Lwt.bind : 'a Lwt.t -> ('a -> 'b Lwt.t) -> 'b Lwt.tandLwt.return : 'a -> 'a Lwt.t. Just in case, though, I will give a brief recap:Lwt.bind promise callbackawaitspromise, and upon resolution, callscallbackwith the result, getting back another promise.Lwt.return datacreates a promise that resolves todata.A monad is a generic type
'a tthat has some functionbind : 'a t -> ('a -> 'b t) -> 'b tand some functionreturn : 'a -> 'a t. (These functions must also follow certain laws, but I digress.) Obviously, the type'a Lwt.twith the functionsLwt.bindandLwt.returnform a monad.Monads are a common functional programming pattern when one wants to represent some kind of "effect" or "computation," in this case asynchronous IO. Monads are powerful because the
bindfunction lets later computations depend on the results of earlier ones. Ifm : 'a trepresents some computation that results in'a, andf : 'a -> 'b tis a function that uses an'ato perform a computation that results in a'b, thenbind m fmakesfdepend on the result ofm.In the case of
Lwt.bind promise callback,callbackdepends on the result ofpromise. The code incallbackcannot run untilpromiseis resolved.When you write
you are really writing
Lwt.bind (f ()) (fun x -> Lwt.bind (g ()) (fun y -> return ())). Becauseg ()is inside the callback, it is not run untilf ()is resolved.Applicatives
A functional programming pattern related to the monad is the applicative. An applicative is a generic type
'a twith a functionmap : ('a -> 'b) -> 'a t -> 'b t, the functionreturn : 'a -> 'a t, and a functionboth : 'a t * 'b t -> ('a * 'b) t. Unlike monads, however, applicatives need not havebind : 'a t -> ('a -> 'b t) -> 'b t, meaning that with applicatives alone, later computations cannot depend on previous ones. All monads are applicatives, but not all applicatives are monads.Because
g ()does not depend on the result off (), your code can be rewritten to useboth:This code translates to
bind (fun (x, y) -> return ()) (both (f ()) (g ())).f ()andg ()appear outside the callback tobind, meaning that they are run immediately and can await in parallel.bothcombinesf ()andg ()into a single promise.The
(let*)and(and*)operators are new to OCaml 4.08. If you are using an earlier version of OCaml, you can just write the translation directly.Lwt.bothin the documentationlet*andand*let*andand*) in the OCaml manual