Module Lwt
Asynchronous programming with promises.
A promise is a placeholder for a single value which might take a long time to compute. Speaking roughly, a promise is a ref that can be filled in later. To make that precise, here is how promises differ from refs:
- A promise might not have a value yet. A promise in this state is called a pending promise.
- Writing a value into a promise is called resolving it. A promise with a value is called a resolved promise.
- Each promise can be resolved only once. After a promise has a value, the promise is immutable.
- It's possible to attach callbacks to a promise. They will run when the promise has a value, i.e. is resolved. If the promise is already resolved when a callback is attached, the callback is run (almost) right away. If the promise is pending, the callback is put into a list and waits.
So, promises are optional, write-once references, and when they don't yet have a value, they store a list of callbacks that are waiting for the value.
The waiting callbacks make promises a natural data type for asynchronous programming. For example, you can ask Lwt to read a file. Lwt immediately returns you only a promise for the data.
You can neglect this promise for a while. You can do some other computation, request more I/O, etc. At some point, you might decide to attach a callback to the read promise, maybe several callbacks.
In the meantime, the read operation is running in the background. Once it finishes, Lwt resolves the read promise by putting the data into it. Lwt then runs the callbacks you attached.
One of those might take the data, and ask Lwt to write it to STDOUT. Lwt gives you a promise for that, too, and the process repeats.
Lwt has a small amount of syntactic sugar to make this look as natural as possible:
let () =
Lwt_main.run begin
let%lwt data = Lwt_io.(read_line stdin) in
let%lwt () = Lwt_io.printl data in
Lwt.return ()
end
(* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix echo.ml && ./a.out *)This is all explained in the next sections:
- Quick start links these concepts to actual functions in Lwt – the most fundamental ones.
- Tutorial shows how to write examples like the above, and how concurrency happens.
- Execution model clarifies control flow when using Lwt.
- Guide to the rest of Lwt shows how everything else in Lwt fits into this framework.
After that is the reference proper, which goes into painful levels of detail on every single type and value in this module, Lwt. Please be safe, and read only what you need from it :)
Happy asynchronous programming!
Quick start
All of Lwt is variations on:
- Promises of type
'aLwt.tare placeholders for values of type'a. Lwt.bindattaches callbacks to promises. When a promise gets a value, its callbacks are called.- Separate resolvers of type
'aLwt.uare used to write values into promises, throughLwt.wakeup_later. - Promises and resolvers are created in pairs using
Lwt.wait. Lwt I/O functions callLwt.waitinternally, but return only the promise. Lwt_main.runis used to wait on one “top-level” promise. When that promise gets a value, the program terminates.
Tutorial
Let's read from STDIN. The first version is written using ordinary values from the OCaml standard library. This makes the program block until the user enters a line:
let () =
let line : string = read_line () in
print_endline "Now unblocked!";
ignore line
(* ocamlfind opt -linkpkg code.ml && ./a.out *)If we use a promise instead, execution continues immediately:
let () =
let line_promise : string Lwt.t =
Lwt_io.(read_line stdin) in
print_endline "Execution just continues...";
ignore line_promise
(* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)Indeed, this program is a little too asynchronous – it exits right away! Let's force it to wait for line_promise at the end by calling Lwt_main.run:
let () =
let line_promise : string Lwt.t =
Lwt_io.(read_line stdin) in
print_endline "Execution just continues...";
let line : string =
Lwt_main.run line_promise in
ignore line
(* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)Lwt_main.run should only be called once, on one promise, at the top level of your program. Most of the time, waiting for promises is done using let%lwt. That is the recommended syntactic sugar for Lwt.bind, and is pronounced “bind”:
let () =
let p : unit Lwt.t =
let%lwt line_1 = Lwt_io.(read_line stdin) in
let%lwt line_2 = Lwt_io.(read_line stdin) in
Lwt_io.printf "%s and %s\n" line_1 line_2
in
Lwt_main.run p
(* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)The way that works is everything in scope after the “in” in “let%lwt x = ... in ...” goes into a callback, and “x” is that callback's argument. So, we could have been very explicit, and written the code like this:
let () =
let p : unit Lwt.t =
let line_1_promise : string Lwt.t = Lwt_io.(read_line stdin) in
Lwt.bind line_1_promise (fun (line_1 : string) ->
let line_2_promise : string Lwt.t = Lwt_io.(read_line stdin) in
Lwt.bind line_2_promise (fun (line_2 : string) ->
Lwt_io.printf "%s and %s\n" line_1 line_2))
in
Lwt_main.run p
(* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)But, as you can see, this is verbose, and the indentation gets a bit crazy. So, we will always use let%lwt.
The code above reads two lines in sequence, because we ask Lwt to wait for line_1, before calling the second Lwt_io.read_line in the callback, to start the second I/O.
We could also run I/O concurrently. All we have to do is not start the second I/O in a callback of the first. Because it doesn't make sense to read two lines from STDIN concurrently, let's start two waits instead:
let () =
Lwt_main.run begin
let three_seconds : unit Lwt.t = Lwt_unix.sleep 3. in
let five_seconds : unit Lwt.t = Lwt_unix.sleep 5. in
let%lwt () = three_seconds in
let%lwt () = Lwt_io.printl "3 seconds passed" in
let%lwt () = five_seconds in
Lwt_io.printl "Only 2 more seconds passed"
end
(* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)This program takes about five seconds to run. We are still new to let%lwt, so let's desugar it:
let () =
Lwt_main.run begin
let three_seconds : unit Lwt.t = Lwt_unix.sleep 3. in
let five_seconds : unit Lwt.t = Lwt_unix.sleep 5. in
(* Both waits have already been started at this point! *)
Lwt.bind three_seconds (fun () ->
(* This is 3 seconds later. *)
Lwt.bind (Lwt_io.printl "3 seconds passed") (fun () ->
Lwt.bind five_seconds (fun () ->
(* Only 2 seconds were left in the 5-second wait, so
this callback runs 2 seconds after the first callback. *)
Lwt_io.printl "Only 2 more seconds passed")))
end
(* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)And that's it! Concurrency in Lwt is simply a matter of whether you start an operation in the callback of another one or not. As a convenience, Lwt provides a few helpers for common concurrency patterns.
Execution model
It's important to understand that promises are a pure-OCaml data type. They don't do any fancy scheduling or I/O. They are just lists of callbacks (if pending), or containers for one value (if resolved).
The interesting function is Lwt_main.run. It's a wrapper around select(2), epoll(7), kqueue(2), or whatever asynchronous I/O API your system provides. On browsers, the work of Lwt_main.run is done by the surrounding JavaScript engine, so you don't call Lwt_main.run from inside your program. But the execution model is still the same, and the description below applies!
To avoid writing out “underlying asynchronous I/O API,” we'll assume, in this section, that the API is select(2). That's just for the sake of abbreviation. It doesn't actually matter, for most purposes, what the underlying I/O API is.
Let's use the program from the tutorial that reads two lines as an example. Here it is, again, in its desugared form:
let () =
let p : unit Lwt.t =
let line_1_promise : string Lwt.t = Lwt_io.(read_line stdin) in
Lwt.bind line_1_promise (fun (line_1 : string) ->
let line_2_promise : string Lwt.t = Lwt_io.(read_line stdin) in
Lwt.bind line_2_promise (fun (line_2 : string) ->
Lwt_io.printf "%s and %s\n" line_1 line_2))
in
Lwt_main.run p
(* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)Lwt_main.run is your program's main I/O loop. You pass it a single promise, and it:
- Uses
select(2)to put your process to sleep until the next I/O completes. - That next I/O happens to be the one that reads
line_1.Lwt_main.runknows that I/O is supposed to resolveline_1_promise, so it putsline_1into the promise and resolves it. - Resolving is an ordinary OCaml operation. It causes all the callbacks of
line_1_promiseto run, one after another. Each callback is also ordinary OCaml code. In our case, there is only one callback, but in general, there might be several, and they might also resolve additional promises. So, promise resolution triggers a “cascade” of callbacks. Eventually, however, we should run out of callbacks, and control will return toLwt_main.run. - In our example, our one callback registers a second I/O with
Lwt_main.run– the one that will readline_2. There are no callbacks left to run after that, so control returns toLwt_main.run. Lwt_main.rungoes back to sleep again by callingselect(2), now waiting for the second I/O that we just registered. The loop repeats itself from step 1.
This has two major implications, one good and one bad. Let's start with the bad one.
(1) If one of your callbacks enters an infinite loop, calls an Lwt-unfriendly blocking I/O, or just runs for a really long time, it won't return control to Lwt_main.run anytime soon. That means Lwt_main.run won't get a chance to resolve any other Lwt I/O promises, even if the underlying I/O operations complete.
In case your callback is just using the CPU for a really long time, you can insert a few calls to Lwt_main.yield into it, and resume your computation in callbacks of yield. This is basically the same as Lwt_unix.sleep 0. – it's a promise that will be resolved by Lwt_main.run after any other I/O resolutions that are already in its queue.
(2) The good implication is that all your callbacks run in a single thread. This means that in most situations, you don't have to worry about locks, synchronization, etc. Anything that is in the same callback is guaranteed to run without interruption. Lwt programs are often much easier to write and refactor, than equivalent programs written with threads – but both are concurrent!
Guide to the rest of Lwt
This module Lwt is the pure-OCaml definition of promises and callback-calling. It has a few extras on top of what's described above:
- Rejection. Lwt promises can actually be resolved in two ways: fulfilled with a value, or rejected with an exception. There is nothing conceptually special about rejection – it's just that you can ask for callbacks to run only on fulfillment, only on rejection, etc.
- Cancellation. This is a special case of rejection, specifically with exception
Lwt.Canceled. It has extra helpers in the Lwt API. - Concurrency helpers. All of these could be implemented on top of
Lwt.bind. As we saw, Lwt concurrency requires only deciding whether to run something inside a callback, or outside it. These functions just implement common patterns, and make intent explicit. - Miscellaneous helpers, and deprecated APIs.
The next layer above module Lwt is the pure-OCaml Lwt “core” library, which provides some promise-friendly patterns, like streams and mvars. This consists of the modules Lwt_list, Lwt_stream, Lwt_result, Lwt_mutex, Lwt_condition, Lwt_mvar, Lwt_pool, and Lwt_switch.
Above that is the Lwt Unix binding, where I/O begins. This includes the module Lwt_main, including the all-important Lwt_main.run. The rest of the Unix binding consists of functions, each one of which...
- ...starts a background I/O operation,
- creates a promise for it and gives it to you,
- registers with
Lwt_main.run, so if you attach callbacks to the promise, they will be called when the I/O operation completes.
The functions are grouped into modules:
Lwt_unixfor Unix system calls.Lwt_bytesfor Unix system calls on bigarrays.Lwt_ioforPervasives-like high-level channels, TCP servers, etc.Lwt_processfor managing subprocesses.Lwt_preemptivefor spawning system threads.- Miscellaneous modules
Lwt_gc,Lwt_engine,Lwt_throttle,Lwt_timeout,Lwt_sys.
Warning! Introductory material ends and detailed reference begins!
Fundamentals
Promises
type +'a tPromises for values of type
'a.A promise is a memory cell that is always in one of three states:
- fulfilled, and containing one value of type
'a, - rejected, and containing one exception, or
- pending, in which case it may become fulfilled or rejected later.
A resolved promise is one that is either fulfilled or rejected, i.e. not pending. Once a promise is resolved, its content cannot change. So, promises are write-once references. The only possible state changes are (1) from pending to fulfilled and (2) from pending to rejected.
Promises are typically “read” by attaching callbacks to them. The most basic functions for that are
Lwt.bind, which attaches a callback that is called when a promise becomes fulfilled, andLwt.catch, for rejection.Promise variables of this type,
'a Lwt.t, are actually read-only in Lwt. Separate resolvers of type'aLwt.uare used to write to them. Promises and their resolvers are created together by callingLwt.wait. There is one exception to this: most promises can be canceled by callingLwt.cancel, without going through a resolver.- fulfilled, and containing one value of type
type -'a uResolvers for promises of type
'aLwt.t.Each resolver can be thought of as the write end of one promise. It can be passed to
Lwt.wakeup_later,Lwt.wakeup_later_exn, orLwt.wakeup_later_resultto resolve that promise.
val wait : unit -> 'a t * 'a uCreates a new pending promise, paired with its resolver.
It is rare to use this function directly. Many helpers in Lwt, and Lwt-aware libraries, call it internally, and return only the promise. You then chain the promises together using
Lwt.bind.However, it is important to understand
Lwt.waitas the fundamental promise “constructor.” All other functions that evaluate to a promise can be, or are, eventually implemented in terms of it.
Resolving
val wakeup_later : 'a u -> 'a -> unitLwt.wakeup_later r vfulfills, with valuev, the pending promise associated with resolverr. This triggers callbacks attached to the promise.If the promise is not pending,
Lwt.wakeup_laterraisesInvalid_argument, unless the promise is canceled. If the promise is canceled,Lwt.wakeup_laterhas no effect.If your program has multiple threads, it is important to make sure that
Lwt.wakeup_later(and any similar function) is only called from the main thread.Lwt.wakeup_latercan trigger callbacks attached to promises by the program, and these assume they are running in the main thread. If you need to communicate from a worker thread to the main thread running Lwt, seeLwt_preemptiveorLwt_unix.send_notification.
val wakeup_later_exn : _ u -> exn -> unitLwt.wakeup_later_exn r exnis likeLwt.wakeup_later, except, if the associated promise is pending, it is rejected withexn.
val return : 'a -> 'a tLwt.return vcreates a new promise that is already fulfilled with valuev.This is needed to satisfy the type system in some cases. For example, in a
matchexpression where one case evaluates to a promise, the other cases have to evaluate to promises as well:match need_input with | true -> Lwt_io.(read_line stdin) (* Has type string Lwt.t... *) | false -> Lwt.return "" (* ...so wrap empty string in a promise. *)Another typical usage is in
let%lwt. The expression after the “in” has to evaluate to a promise. So, if you compute an ordinary value instead, you have to wrap it:let%lwt line = Lwt_io.(read_line stdin) in Lwt.return (line ^ ".")
val fail : exn -> _ tLwt.fail exnis likeLwt.return, except the new promise that is already rejected withexn.Whenever possible, it is recommended to use
raise exninstead, asraisecaptures a backtrace, whileLwt.faildoes not. If you callraise exnin a callback that is expected by Lwt to return a promise, Lwt will automatically wrapexnin a rejected promise, but the backtrace will have been recorded by the OCaml runtime. UseLwt.failonly when you specifically want to create a rejected promise, to pass to another function, or store in a data structure.
Callbacks
val bind : 'a t -> ('a -> 'b t) -> 'b tLwt.bind p_1 fmakes it so thatfwill run whenp_1is fulfilled.When
p_1is fulfilled with valuev_1, the callbackfis called with that same valuev_1. Eventually, after perhaps starting some I/O or other computation,freturns promisep_2.Lwt.binditself returns immediately. It only attaches the callbackftop_1– it does not wait forp_2. WhatLwt.bindreturns is yet a third promise,p_3. Roughly speaking, fulfillment ofp_3represents bothp_1andp_2becoming fulfilled, one after the other.A minimal example of this is an echo program:
let () = let p_3 = Lwt.bind Lwt_io.(read_line stdin) (fun line -> Lwt_io.printl line) in Lwt_main.run p_3 (* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)Rejection of
p_1andp_2, and raising an exception inf, are all forwarded to rejection ofp_3.Precise behavior
Lwt.bindreturns a promisep_3immediately.p_3starts out pending, and is resolved as follows:- The first condition to wait for is that
p_1becomes resolved. It does not matter whetherp_1is already resolved whenLwt.bindis called, or becomes resolved later – the rest of the behavior is the same. - If and when
p_1becomes resolved, it will, by definition, be either fulfilled or rejected. - If
p_1is rejected,p_3is rejected with the same exception. - If
p_1is fulfilled, with valuev,fis applied tov. fmay finish by returning the promisep_2, or raising an exception.- If
fraises an exception,p_3is rejected with that exception. - Finally, the remaining case is when
freturnsp_2. From that point on,p_3is effectively made into a reference top_2. This means they have the same state, undergo the same state changes, and performing any operation on one is equivalent to performing it on the other.
Syntactic sugar
Lwt.bindis almost never written directly, because sequences ofLwt.bindresult in growing indentation and many parentheses:let () = Lwt_main.run begin Lwt.bind Lwt_io.(read_line stdin) (fun line -> Lwt.bind (Lwt_unix.sleep 1.) (fun () -> Lwt_io.printf "One second ago, you entered %s\n" line)) end (* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)The recommended way to write
Lwt.bindis using thelet%lwtsyntactic sugar:let () = Lwt_main.run begin let%lwt line = Lwt_io.(read_line stdin) in let%lwt () = Lwt_unix.sleep 1. in Lwt_io.printf "One second ago, you entered %s\n" line end (* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)This uses the Lwt PPX (preprocessor). Note that we had to add package
lwt_ppxto the command line for building this program. We will do that throughout this manual.Another way to write
Lwt.bind, that you may encounter while reading code, is with the>>=operator:open Lwt.Infix let () = Lwt_main.run begin Lwt_io.(read_line stdin) >>= fun line -> Lwt_unix.sleep 1. >>= fun () -> Lwt_io.printf "One second ago, you entered %s\n" line end (* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)The
>>=operator comes from the moduleLwt.Infix, which is why we opened it at the beginning of the program.See also
Lwt.map.- The first condition to wait for is that
Rejection
val catch : (unit -> 'a t) -> (exn -> 'a t) -> 'a tLwt.catch f happliesf (), which returns a promise, and then makes it so thath(“handler”) will run when that promise is rejected.let () = Lwt_main.run begin Lwt.catch (fun () -> Lwt.fail Exit) (function | Exit -> Lwt_io.printl "Got Stdlib.Exit" | exn -> Lwt.fail exn) end (* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)Despite the above code, the recommended way to write
Lwt.catchis using thetry%lwtsyntactic sugar from the PPX. Here is an equivalent example:let () = Lwt_main.run begin try%lwt Lwt.fail Exit with Exit -> Lwt_io.printl "Got Stdlb.Exit" end (* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)A particular advantage of the PPX syntax is that it is not necessary to artificially insert a catch-all
exn -> Lwt.fail exncase. Like in the core language'stryexpression, the catch-all case is implied intry%lwt.Lwt.catchis a counterpart toLwt.bind–Lwt.bindis for fulfillment, andLwt.catchis for rejection.As with
Lwt.bind, three promises are involved:p_1, the promise returned from applyingf ().p_2, the promise returned from applyingh exn.p_3, the promise returned byLwt.catchitself.
The remainder is (1) a precise description of how
p_3is resolved, and (2) a warning about accidentally using ordinarytryfor exception handling in asynchronous code.(1)
Lwt.catchfirst appliesf (). It then returnsp_3immediately.p_3starts out pending. It is resolved as follows:- If
f ()returned a promisep_1, andp_1becomes fulfilled,p_3is fulfilled with the same value. p_1can instead become rejected. There is one other possibility:f ()itself raised an exception, instead of returning a promise. The behavior ofLwt.catchis the same whetherf ()raised an exception, or returned a promise that is later rejected with an exception. Let's call the exceptionexn.h exnis applied.h exnmay return a promise, or might itself raise an exception. The first case is the interesting one, but the exception case is simple, so we cover the exception case first.- If
h exnraises another exceptionexn',p_3is rejected withexn'. - If
h exninstead returns the promisep_2,p_3is effectively made into a reference top_2. This meansp_3andp_2have the same state, undergo the same state changes, and performing any operation one is equivalent to performing it on the other.
(2) Warning: it may be tempting to write this code, which differs from the second example above only in that
tryis used instead oftry%lwt:let () = Lwt_main.run begin try Lwt.fail Exit with Exit -> Lwt_io.printl "Got Stdlib.Exit" end (* ocamlfind opt -linkpkg -thread -package lwt.unix code.ml && ./a.out *)This does not handle the exception and does not print the message. Instead, it terminates the program with an unhandled
Stdlib.Exit.This is because the call to
Lwt.failcreates a rejected promise. The promise is still an ordinary OCaml value, though, and not a raised exception. So,tryconsiders that code to have succeeded, and doesn't run the handler. When that rejected promise reachesLwt_main.run, it isLwt_main.runthat raises the exception.Basically, the rule is: if the code inside
tryevaluates to a promise (has type_ Lwt.t), replacetrybytry%lwt.
val finalize : (unit -> 'a t) -> (unit -> unit t) -> 'a tLwt.finalize f cappliesf (), which returns a promise, and then makes it soc(“cleanup”) will run when that promise is resolved.In other words,
cruns no matter whether promisef ()is fulfilled or rejected. As the names suggest,Lwt.finalizecorresponds to thefinallyconstruct found in many programming languages, andcis typically used for cleaning up resources:let () = Lwt_main.run begin let%lwt file = Lwt_io.(open_file Input "code.ml") in Lwt.finalize (fun () -> let%lwt content = Lwt_io.read file in Lwt_io.print content) (fun () -> Lwt_io.close file) end (* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)As with
Lwt.bindandLwt.catch, there is a syntactic sugar forLwt.finalize, though it is not as often used:let () = Lwt_main.run begin let%lwt file = Lwt_io.(open_file Input "code.ml") in begin let%lwt content = Lwt_io.read file in Lwt_io.print content end [%lwt.finally Lwt_io.close file] end (* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)Also as with
Lwt.bindandLwt.catch, three promises are involved:p_1, the promise returned from applyingf ().p_2, the promise returned from applyingc ().p_3, the promise returned byLwt.finalizeitself.
p_3is returned immediately. It starts out pending, and is resolved as follows:f ()is applied. If it finishes, it will either return a promisep_1, or raise an exception.- If
f ()raises an exception,p_1is created artificially as a promise rejected with that exception. So, no matter howf ()finishes, there is a promisep_1representing the outcome. - After
p_1is resolved (fulfilled or rejected),c ()is applied. This is meant to be the cleanup code. - If
c ()finishes, it will also either return a promise,p_2, or raise an exception. - If
c ()raises an exception,p_2is created artificially as a promise rejected with that exception. Again, no matter howc ()finishes, there is a promisep_2representing the outcome of cleanup. - If
p_2is fulfilled,p_3is resolved the same wayp_1had been resolved. In other words,p_1is forwarded top_2when cleanup is successful. - If
p_2is rejected,p_3is rejected with the same exception. In other words, when cleanup fails,p_3is rejected. Note this means that if both the protected code and the cleanup fail, the cleanup exception has precedence.
val try_bind : (unit -> 'a t) -> ('a -> 'b t) -> (exn -> 'b t) -> 'b tLwt.try_bind f g happliesf (), and then makes it so that:Lwt.try_bindis a generalizedLwt.finalize. The difference is thatLwt.try_bindruns different callbacks depending on howf ()is resolved. This has two main implications:- The cleanup functions
gandheach “know” whetherf ()was fulfilled or rejected. - The cleanup functions
gandhare passed the valuef ()was fulfilled with, and, respectively, the exceptionf ()was rejected with.
The rest is a detailed description of the promises involved.
As with
Lwt.finalizeand the several preceding functions, three promises are involved.p_1is the promise returned from applyingf ().p_2is the promise returned from applyinghorg, depending on which one is chosen.p_3is the promise returned byLwt.try_binditself.
Lwt.try_bindreturnsp_3immediately.p_3starts out pending, and is resolved as follows:f ()is applied. If it finishes, it either returnsp_1, or raises an exception.- If
f ()raises an exception,p_1is created artificially as a promise rejected with that exception. So, no matter howf ()finishes, there is a promisep_1representing the outcome. - If
p_1is fulfilled,gis applied to the valuep_1is fulfilled with. - If
p_1is rejected,his applied to the exceptionp_1is rejected with. - So, in either case, a callback is applied. The rest of the procedure is the same no matter which callback was chosen, so we will refer to it as “the callback.”
- If the callback finishes, it either returns
p_2, or raises an exception. - If the callback raises an exception,
p_3is rejected with that exception. - If the callback returns
p_2,p_3is effectively made into an reference top_2. They have the same state, including any state changes, and performing any operation on one is equivalent to performing it on the other.
- The cleanup functions
val async : (unit -> unit t) -> unitLwt.async fappliesf (), which returns a promise, and then makes it so that if the promise is rejected, the exception is passed to!Lwt.async_exception_hook.In addition, if
f ()raises an exception, it is also passed to!Lwt.async_exception_hook.!Lwt.async_exception_hooktypically prints an error message and terminates the program.Lwt.asyncis misleadingly named. Itself, it has nothing to do with asynchronous execution. It's actually a safety function for making Lwt programs more debuggable.For example, take this program, which prints messages in a loop, while waiting for one line of user input:
let () = let rec show_nag () : _ Lwt.t = let%lwt () = Lwt_io.printl "Please enter a line" in let%lwt () = Lwt_unix.sleep 1. in show_nag () in ignore (show_nag ()); (* Bad – see note for (1)! *) Lwt_main.run begin let%lwt line = Lwt_io.(read_line stdin) in Lwt_io.printl line end (* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)If one of the I/O operations in
show_nagwere to fail, the promise representing the whole loop would get rejected. However, since we are ignoring that promise at (1), we never find out about the rejection. If this failure and resulting rejection represents a bug in the program, we have a harder time finding out about the bug.A safer version differs only in using
Lwt.asyncinstead ofStdlib.ignore:let () = let rec show_nag () : _ Lwt.t = let%lwt () = Lwt_io.printl "Please enter a line" in let%lwt () = Lwt_unix.sleep 1. in show_nag () in Lwt.async (fun () -> show_nag ()); Lwt_main.run begin let%lwt line = Lwt_io.(read_line stdin) in Lwt_io.printl line end (* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)In this version, if I/O in
show_nagfails with an exception, the exception is printed byLwt.async, and then the program exits.The general rule for when to use
Lwt.asyncis:- Promises which are not passed to
Lwt.bind,Lwt.catch,Lwt.join, etc., are top-level promises. - One top-level promise is passed to
Lwt_main.run, as can be seen in most examples in this manual. - Every other top-level promise should be wrapped in
Lwt.async.
- Promises which are not passed to
val async_exception_hook : (exn -> unit) Stdlib.refReference to a function, to be called on an "unhandled" exception.
This reference is used by
Lwt.async,Lwt.on_cancel,Lwt.on_success,Lwt.on_failure,Lwt.on_termination,Lwt.on_any, and the deprecatedLwt.ignore_result.The initial, default implementation prints the exception, then terminates the process with non-zero exit status, as if the exception had reached the top level of the program:
let () = Lwt.async (fun () -> Lwt.fail Exit) (* ocamlfind opt -linkpkg -package lwt code.ml && ./a.out *)produces in the output:
Fatal error: exception Stdlib.Exit
If you are writing an application, you are welcome to reassign the reference, and replace the function with something more appropriate for your needs.
If you are writing a library, you should leave this reference alone. Its behavior should be determined by the application.
Concurrency
Multiple wait
val both : 'a t -> 'b t -> ('a * 'b) tLwt.both p_1 p_2returns a promise that is pending until both promisesp_1andp_2become resolved.let () = let p_1 = let%lwt () = Lwt_unix.sleep 3. in Lwt_io.printl "Three seconds elapsed" in let p_2 = let%lwt () = Lwt_unix.sleep 5. in Lwt_io.printl "Five seconds elapsed" in let p_3 = Lwt.both p_1 p_2 in Lwt_main.run p_3 (* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)If both
p_1andp_2become fulfilled,Lwt.both p_1 p_2is also fulfilled, with the pair of their final values. Otherwise, if at least one of the two promises becomes rejected,Lwt.both p_1 p_2is rejected with the same exception as one such promise, chosen arbitrarily. Note that this occurs only after both promises are resolved, not immediately when the first promise is rejected.- since
- 4.2.0
val join : unit t list -> unit tLwt.join psreturns a promise that is pending until all promises in the listpsbecome resolved.let () = let p_1 = let%lwt () = Lwt_unix.sleep 3. in Lwt_io.printl "Three seconds elapsed" in let p_2 = let%lwt () = Lwt_unix.sleep 5. in Lwt_io.printl "Five seconds elapsed" in let p_3 = Lwt.join [p_1; p_2] in Lwt_main.run p_3 (* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)If all of the promises in
psbecome fulfilled,Lwt.join psis also fulfilled. Otherwise, if at least one promise inpsbecomes rejected,Lwt.join psis rejected with the same exception as one such promise, chosen arbitrarily. Note that this occurs only after all the promises are resolved, not immediately when the first promise is rejected.
val all : 'a t list -> 'a list tLwt.all psis likeLwt.joinps: it waits for all promises in the listpsto become resolved.It then resolves the returned promise with the list of all resulting values.
Note that if any of the promises in
psis rejected, the returned promise is also rejected. This means that none of the values will be available, even if some of the promises inpswere already resolved when one of them is rejected. For more fine-grained handling of rejection, structure the program withLwt_streamorLwt_list, handle rejections explicitly, or useLwt.joinand collect values manually.- since
- 5.1.0
Racing
val pick : 'a t list -> 'a tLwt.pick psreturns a promise that is pending until one promise in the listpsbecomes resolved.When at least one promise in
psis resolved,Lwt.picktries to cancel all other promises that are still pending, usingLwt.cancel.let () = let echo = let%lwt line = Lwt_io.(read_line stdin) in Lwt_io.printl line in let timeout = Lwt_unix.sleep 5. in Lwt_main.run (Lwt.pick [echo; timeout]) (* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)If the first promise in
psto become resolved is fulfilled, the result promisepis also fulfilled, with the same value. Likewise, if the first promise inpsto become resolved is rejected,pis rejected with the same exception.If
pshas no promises (if it is the empty list),Lwt.pick psraisesStdlib.Invalid_argument _.It's possible for multiple promises in
psto become resolved simultaneously. This happens most often when some promisespsare already resolved at the timeLwt.pickis called.In that case, if at least one of the promises is rejected, the result promise
pis rejected with the same exception as one such promise, chosen arbitrarily. If all promises are fulfilled,pis fulfilled with the value of one of the promises, also chosen arbitrarily.The remaining functions in this section are variations on
Lwt.pick.
val choose : 'a t list -> 'a tLwt.choose psis the same asLwt.pickps, except that it does not try to cancel pending promises inps.
val npick : 'a t list -> 'a list tLwt.npick psis similar toLwt.pickps, the difference being that when multiple promises inpsare fulfilled simultaneously (and none are rejected), the result promise is fulfilled with the list of values the promises were fulfilled with.When at least one promise is rejected,
Lwt.npickstill rejects the result promise with the same exception.
val nchoose : 'a t list -> 'a list tLwt.nchoose psis the same asLwt.npickps, except that it does not try to cancel pending promises inps.
val nchoose_split : 'a t list -> ('a list * 'a t list) tLwt.nchoose_split psis the same asLwt.nchooseps, except that when multiple promises inpsare fulfilled simultaneously (and none are rejected), the result promise is fulfilled with both the list of values of the fulfilled promises, and the list of promises that are still pending.
Cancellation
Note: cancelation has proved difficult to understand, explain, and maintain, so use of these functions is discouraged in new code. See ocsigen/lwt#283.
exceptionCanceledCanceled promises are those rejected with this exception,
Lwt.Canceled. SeeLwt.cancel.
val task : unit -> 'a t * 'a uLwt.taskis the same asLwt.wait, except the resulting promisepis cancelable.This is significant, because it means promises created by
Lwt.taskcan be resolved (specifically, rejected) by canceling them directly, in addition to being resolved through their paired resolvers.In contrast, promises returned by
Lwt.waitcan only be resolved through their resolvers.
val cancel : _ t -> unitLwt.cancel pattempts to cancel the pending promisep, without needing access to its resolver.It is recommended to avoid
Lwt.cancel, and handle cancelation by tracking the needed extra state explicitly within your library or application.A canceled promise is one that has been rejected with exception
Lwt.Canceled.There are straightforward ways to make promises canceled. One could create a promise that starts out canceled, with
Lwt.failLwt.Canceled. It's also possible to make a promise canceled through its resolver, by callingLwt.wakeup_later_exnr Lwt.Canceled.This function,
Lwt.cancel, provides another method, which can cancel pending promises without going through their resolvers – it acts directly on promises.Like any other promise rejection, the canceled state of a promise is propagated “forwards” by
Lwt.bind,Lwt.join, etc., as described in the documentation of those functions.Cancellation is a separate phase, triggered only by
Lwt.cancel, that searches backwards, strating fromp, for promises to reject withLwt.Canceled. Once those promises are found, they are canceled, and then ordinary, forwards rejection propagation takes over.All of this will be made precise, but first let's have an example:
let () = let p = let%lwt () = Lwt_unix.sleep 5. in Lwt_io.printl "Slept five seconds" in Lwt.cancel p; Lwt_main.run p (* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)At the time
Lwt.cancelis called,p“depends” on thesleeppromise (theprintlis not yet called, so its promise hasn't been created).So,
Lwt.cancelrecursively tries to cancel thesleeppromise. That is an example of the backwards search. Thesleeppromise is a pending promise that doesn't depend on anything, so backwards search stops at it. The state of thesleeppromise is set to rejected withLwt.Canceled.Lwt.bindthen propagates the rejection forwards top, sopalso becomes canceled.Eventually, this rejection reaches
Lwt_main.run, which raises theLwt.Canceledas an ordinary exception. Thesleepdoes not complete, and theprintlis never started.Promises, like the
sleeppromise above, that can be rejected byLwt.cancelare cancelable. Most promises in Lwt are either cancelable, or depend on cancelable promises. The functionsLwt.waitandLwt.no_cancelcreate promises that are not cancelable.The rest is a detailed description of how the
Lwt.cancelbackwards search works.- If
pis already resolved,Lwt.canceldoes nothing. - If
pwas created byLwt.waitorLwt.no_cancel,Lwt.canceldoes nothing. - If
pwas created byLwt.taskorLwt.protected,Lwt.cancelrejects it withLwt.Canceled. This rejection then propagates normally through any Lwt calls that depend onp. Most I/O promises are internally created by callingLwt.task. - Suppose
p_3was returned byLwt.bind,Lwt.map,Lwt.catch,Lwt.finalize, orLwt.try_bind. Then, see those functions for the naming of the other promises involved. Ifp_3is pending, then eitherp_1is pending, orp_2is pending.Lwt.cancel p_3then tries recursively to cancel whichever of these two is still pending. If that succeeds,p_3may be canceled later by the normal propagation of rejection. - Suppose
pwas returned byLwt.join,Lwt.pick, or similar function, which was applied to the promise listps.Lwt.cancelthen recursively tries to cancel each promise inps. If one of those cancellations succeeds,pmay be canceled later by the normal propagation of rejection.
- If
val on_cancel : _ t -> (unit -> unit) -> unitLwt.on_cancel p fmakes it so thatfwill run whenpbecomes canceled.Callbacks scheduled with
on_cancelare guaranteed to run before any other callbacks that are triggered by rejection, such as those added byLwt.catch.Note that this does not interact directly with the cancellation mechanism, the backwards search described in
Lwt.cancel. For example, manually rejecting a promise withLwt.Canceledis sufficient to triggerf.fshould not raise exceptions. If it does, they are passed to!Lwt.async_exception_hook, which terminates the process by default.
val protected : 'a t -> 'a tLwt.protected pcreates a cancelable promisep'with the same state asp. However, cancellation, the backwards search described inLwt.cancel, stops atp', and does not continue top.
val no_cancel : 'a t -> 'a tLwt.no_cancel pcreates a non-cancelable promisep', with the same state asp. Cancellation, the backwards search described inLwt.cancel, stops atp', and does not continue top.Note that
p'can still be canceled ifpis canceled.Lwt.no_cancelonly prevents cancellation ofpandp'throughp'.
Convenience
Callback helpers
val map : ('a -> 'b) -> 'a t -> 'b tLwt.map f p_1is similar toLwt.bindp_1 f, butfis not expected to return a promise.This function is more convenient that
Lwt.bindwhenfinherently does not return a promise. An example isStdlib.int_of_string:let read_int : unit -> int Lwt.t = fun () -> Lwt.map int_of_string Lwt_io.(read_line stdin) let () = Lwt_main.run begin let%lwt number = read_int () in Lwt_io.printf "%i\n" number end (* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)By comparison, the
Lwt.bindversion is more awkward:let read_int : unit -> int Lwt.t = fun () -> Lwt.bind Lwt_io.(read_line stdin) (fun line -> Lwt.return (int_of_string line))As with
Lwt.bind, sequences of calls toLwt.mapresult in excessive indentation and parentheses. The recommended syntactic sugar for avoiding this is the>|=operator, which comes from moduleLwt.Infix:open Lwt.Infix let read_int : unit -> int Lwt.t = fun () -> Lwt_io.(read_line stdin) >|= int_of_stringThe detailed operation follows. For consistency with the promises in
Lwt.bind, the two promises involved are namedp_1andp_3:p_1is the promise passed toLwt.map.p_3is the promise returned byLwt.map.
Lwt.mapreturns a promisep_3.p_3starts out pending. It is resolved as follows:p_1may be, or become, resolved. In that case, by definition, it will become fulfilled or rejected. Fulfillment is the interesting case, but the behavior on rejection is simpler, so we focus on rejection first.- When
p_1becomes rejected,p_3is rejected with the same exception. - When
p_1instead becomes fulfilled, call the value it is fulfilled withv. f vis applied. If this finishes, it may either return another value, or raise an exception.- If
f vreturns another valuev',p_3is fulfilled withv'. - If
f vraises exceptionexn,p_3is rejected withexn.
val on_success : 'a t -> ('a -> unit) -> unitLwt.on_success p fmakes it so thatfwill run whenpis fulfilled.It is similar to
Lwt.bind, except no new promises are created.fis a plain, arbitrary function attached top, to perform some side effect.If
fraises an exception, it is passed to!Lwt.async_exception_hook. By default, this will terminate the process.
val on_failure : _ t -> (exn -> unit) -> unitLwt.on_failure p fmakes it so thatfwill run whenpis rejected.It is similar to
Lwt.catch, except no new promises are created.If
fraises an exception, it is passed to!Lwt.async_exception_hook. By default, this will terminate the process.
val on_termination : _ t -> (unit -> unit) -> unitLwt.on_termination p fmakes it so thatfwill run whenpis resolved – that is, fulfilled or rejected.It is similar to
Lwt.finalize, except no new promises are created.If
fraises an exception, it is passed to!Lwt.async_exception_hook. By default, this will terminate the process.
val on_any : 'a t -> ('a -> unit) -> (exn -> unit) -> unitLwt.on_any p f gmakes it so that:It is similar to
Lwt.try_bind, except no new promises are created.If
forgraise an exception, the exception is passed to!Lwt.async_exception_hook. By default, this will terminate the process.
Infix operators
module Infix : sig ... endThis module provides several infix operators for making programming with Lwt more convenient.
module Syntax : sig ... end
Pre-allocated promises
val return_unit : unit tLwt.return_unitis defined asLwt.return(), but this definition is evaluated only once, during initialization of moduleLwt, at the beginning of your program.This means the promise is allocated only once. By contrast, each time
Lwt.return()is evaluated, it allocates a new promise.It is recommended to use
Lwt.return_unitonly where you know the allocations caused by an instance ofLwt.return()are a performance bottleneck. Generally, the cost of I/O tends to dominate the cost ofLwt.return()anyway.In future Lwt, we hope to perform this optimization, of using a single, pre-allocated promise, automatically, wherever
Lwt.return()is written.
val return_none : _ option tLwt.return_noneis likeLwt.return_unit, but forLwt.returnNone.
val return_nil : _ list tLwt.return_nilis likeLwt.return_unit, but forLwt.return[].
val return_true : bool tLwt.return_trueis likeLwt.return_unit, but forLwt.returntrue.
val return_false : bool tLwt.return_falseis likeLwt.return_unit, but forLwt.returnfalse.
Result type
type +'a result= ('a, exn) Result.resultRepresentation of the content of a resolved promise of type
'aLwt.t.This type is effectively
type +'a Lwt.result = | Ok of 'a | Error of exnor, on OCaml 4.02:
type +'a Lwt.result = | Result.Ok of 'a | Result.Error of exnA resolved promise of type
'aLwt.tis either fulfilled with a value of type'a, or rejected with an exception.This corresponds to the cases of a
('a, exn)Stdlib.result: fulfilled corresponds toOk of 'a, and rejected corresponds toError of exn.It's important to note that this type constructor,
Lwt.result, is different fromStdlib.result. It is a specialization ofStdlib.resultso that theErrorconstructor always carriesexn.For Lwt programming with
resultwhere theErrorconstructor can carry arbitrary error types, see moduleLwt_result.The naming conflict between
Lwt.resultandStdlib.resultis an unfortunate historical accident.Stdlib.resultdid not exist whenLwt.resultwas created.The type
Result.resultis equivalent toStdlib.resultstarting from OCaml 4.03. If you need compatibility with OCaml 4.02, refer toStdlib.resultasResult.result, and prefix the constructor names withResult, as shown in the second example.
val of_result : 'a result -> 'a tLwt.of_result rconverts an r to a resolved promise.- If
risOk v,Lwt.of_result risLwt.return v, i.e. a promise fulfilled withv. - If
risError exn,Lwt.of_result risLwt.fail exn, i.e. a promise rejected withexn.
- If
val wakeup_later_result : 'a u -> 'a result -> unitLwt.wakeup_later_result r resultresolves the pending promisepassociated to resolverr, according toresult:- If
resultisOk v,pis fulfilled withv. - If
resultisError exn,pis rejected withexn.
If
pis not pending,Lwt.wakeup_later_resultraisesStdlib.Invalid_argument _, except ifpis canceled. Ifpis canceled,Lwt.wakeup_later_resulthas no effect.- If
State query
Deprecated
Implicit callback arguments
Using this mechanism is discouraged, because it is non-syntactic, and because it manipulates hidden state in module Lwt. It is recommended instead to pass additional values explicitly in tuples, or maintain explicit associative maps for them.
type 'a keyKeys into the implicit callback argument map, for implicit arguments of type
'a option.The keys are abstract, but they are basically integers that are all distinct from each other.
See
Lwt.with_value.
val new_key : unit -> 'a keyCreates a fresh implicit callback argument key.
The key is distinct from any other key created by the current process. The value
Noneof type'a optionis immediately associated with the key.See
Lwt.with_value.
val get : 'a key -> 'a optionRetrieves the value currently associated with the given implicit callback argument key.
See
Lwt.with_value.
val with_value : 'a key -> 'a option -> (unit -> 'b) -> 'bLwt.with_value k v fsetsktovin Lwt's internal implicit callback argument map, then runsf (), then restores the previous value associated withk.Lwt maintains a single, global map, that can be used to “pass” extra arguments to callbacks:
let () = let k : string Lwt.key = Lwt.new_key () in let say_hello () = match Lwt.get k with | None -> assert false | Some s -> Lwt_io.printl s in Lwt_main.run begin Lwt.with_value k (Some "Hello world!") begin fun () -> Lwt.bind (Lwt_unix.sleep 1.) (fun () -> say_hello ()) end end (* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)Note that the string
Hello world!was passed tosay_hellothrough the keyk. Meanwhile, the only explicit argument of the callbacksay_hellois().The way this works is functions like
Lwt.bindtake a snapshot of the implicit argument map. Later, right before the callback is run, the map is restored to that snapshot. In other words, the map has the same state inside the callback as it did at the time the callback was registered.To be more precise:
Lwt.with_valueassociatesSome "Hello world!"withk, and runs the function passed to it.- This function contains the
Lwt.bind. - OCaml's eager evaluation means the arguments are evaluated first. In particular, the
Lwt_unix.sleep 1.promise is created. Lwt.bindthen attaches the callback in its second argument, the one which callssay_hello, to thatsleeppromise.Lwt.bindalso takes a snapshot of the current state of the implicit argument map, and pairs the callback with that snapshot.- The callback will not run for another second or so, when the
sleeppromise will be resolved. - Instead,
Lwt.bindreturns its result promisep_3. This causesLwt.with_valueto also returnp_3, first restoringkto be associated withNone. Lwt_main.rungets the pendingp_3, and blocks the whole process, withkassociated withNone.- One second later, the
sleepI/O completes, resolving thesleeppromise. - This triggers the
say_hellocallback. Right before the callback is called, the implicit argument map is restored to its snapshot, sokis associated withSome "Hello world!". - After the callback completes, Lwt again restores
kto be associated withNone.
The Lwt functions that take snapshots of the implicit callback argument map are exactly those which attach callbacks to promises:
Lwt.bindand its variants>>=andlet%lwt,Lwt.mapand its variant>|=,Lwt.catchand its varianttry%lwt,Lwt.finalizeand its variant%lwt.finally,Lwt.try_bind,Lwt.on_success,Lwt.on_failure,Lwt.on_termination, andLwt.on_any.Lwt.with_valueshould only be called in the main thread, i.e. do not call it insideLwt_preemptive.detach.
Immediate resolving
val wakeup : 'a u -> 'a -> unitLwt.wakeup r vis likeLwt.wakeup_laterr v, except it guarantees that callbacks associated withrwill be called immediately, deeper on the current stack.In contrast,
Lwt.wakeup_latermay call callbacks immediately, or may queue them for execution on a shallower stack – though still before the next time Lwt blocks the process on I/O.Using this function is discouraged, because calling it in a loop can exhaust the stack. The loop might be difficult to detect or predict, due to combined mutually-recursive calls between multiple modules and libraries.
Also, trying to use this function to guarantee the timing of callback calls for synchronization purposes is discouraged. This synchronization effect is obscure to readers. It is better to use explicit promises, or
Lwt_mutex,Lwt_condition, and/orLwt_mvar.
val wakeup_exn : _ u -> exn -> unitLwt.wakeup_exn r exnis likeLwt.wakeup_later_exnr exn, but has the same problems asLwt.wakeup.
val wakeup_result : 'a u -> 'a result -> unitLwt.wakeup_result r resultis likeLwt.wakeup_later_resultr result, but has the same problems asLwt.wakeup.
Helpers for resolving
val make_value : 'a -> 'a resultLwt.make_value vis equivalent toOk vsince OCaml 4.03. If you need compatibility with OCaml 4.02, useResult.Okand depend on opam packageresult.
Linked lists of promises
val add_task_r : 'a u Lwt_sequence.t -> 'a tLwt.add_task_r sequenceis equivalent tolet p, r = Lwt.task () in let node = Lwt_sequence.add_r r sequence in Lwt.on_cancel p (fun () -> Lwt_sequence.remove node); pUse of this function is discouraged for two reasons:
Lwt_sequenceshould not be used outside Lwt.- This function only exists because it performs a minor internal optimization, which may be removed.
val add_task_l : 'a u Lwt_sequence.t -> 'a tLike
Lwt.add_task_r, but the equivalent code callsLwt_sequence.add_linstead.
Yielding
val pause : unit -> unit tLwt.pause ()creates a pending promise that is fulfilled after Lwt finishes calling all currently ready callbacks, i.e. it is fulfilled on the next “tick.”Putting the rest of your computation into a callback of
Lwt.pause ()creates a “yield” that gives other callbacks a chance to run first.For example, to break up a long-running computation, allowing I/O to be handled between chunks:
let () = let rec handle_io () = let%lwt () = Lwt_io.printl "Handling I/O" in let%lwt () = Lwt_unix.sleep 0.1 in handle_io () in let rec compute n = if n = 0 then Lwt.return () else let%lwt () = if n mod 1_000_000 = 0 then Lwt.pause () else Lwt.return () in compute (n - 1) in Lwt.async handle_io; Lwt_main.run (compute 100_000_000) (* ocamlfind opt -linkpkg -thread -package lwt_ppx,lwt.unix code.ml && ./a.out *)If you replace the call to
Lwt.pausebyLwt.returnin the program above,"Handling I/O"is printed only once. WithLwt.pause, it is printed several times, depending on the speed of your machine.An alternative way to handle long-running computations is to detach them to preemptive threads using
Lwt_preemptive.
Function lifters
val wrap : (unit -> 'a) -> 'a tLwt.wrap fappliesf (). Iff ()returns a valuev,Lwt.wrapreturnsLwt.returnv. Iff ()raises an exception exn,Lwt.wrapreturnsLwt.failexn.
val wrap1 : ('a -> 'b) -> 'a -> 'b tval wrap2 : ('a -> 'b -> 'c) -> 'a -> 'b -> 'c tval wrap3 : ('a -> 'b -> 'c -> 'd) -> 'a -> 'b -> 'c -> 'd tval wrap4 : ('a -> 'b -> 'c -> 'd -> 'e) -> 'a -> 'b -> 'c -> 'd -> 'e tval wrap5 : ('a -> 'b -> 'c -> 'd -> 'e -> 'f) -> 'a -> 'b -> 'c -> 'd -> 'e -> 'f tval wrap6 : ('a -> 'b -> 'c -> 'd -> 'e -> 'f -> 'g) -> 'a -> 'b -> 'c -> 'd -> 'e -> 'f -> 'g tval wrap7 : ('a -> 'b -> 'c -> 'd -> 'e -> 'f -> 'g -> 'h) -> 'a -> 'b -> 'c -> 'd -> 'e -> 'f -> 'g -> 'h tAs a “prototype,”
Lwt_wrap1 fcreates a promise-valued functiong:let g v = try let v' = f v in Lwt.return v' with exn -> Lwt.fail exnThe remainder of the functions work analogously – they just work on
fwith larger numbers of arguments.Note that there is an important difference to
Lwt.wrap. These functions don't runf, nor create the final promise, immediately. In contrast,Lwt.wrapruns its argumentfeagerly.To get a suspended function instead of the eager execution of
Lwt.wrap, useLwt.wrap1.
Trivial promises
val return_some : 'a -> 'a option tCounterpart to
Lwt.return_none. However, unlikeLwt.return_none, this function performs no optimization. This is because it takes an argument, so it cannot be evaluated at initialization time, at which time the argument is not yet available.
val return_ok : 'a -> ('a, _) Result.result tLike
Lwt.return_some, this function performs no optimization.- since
- Lwt 2.6.0
val return_error : 'e -> (_, 'e) Result.result tLike
Lwt.return_some, this function performs no optimization.- since
- Lwt 2.6.0
Unscoped infix operators
val (>>=) : 'a t -> ('a -> 'b t) -> 'b tval (>|=) : 'a t -> ('a -> 'b) -> 'b tval (<?>) : 'a t -> 'a t -> 'a tval (<&>) : unit t -> unit t -> unit tval (=<<) : ('a -> 'b t) -> 'a t -> 'b tval (=|<) : ('a -> 'b) -> 'a t -> 'b tUse the operators in module
Lwt.Infixinstead. Using these instances of the operators directly requires opening moduleLwt, which brings an excessive number of other names into scope.
Miscellaneous
val ignore_result : _ t -> unitAn obsolete variant of
Lwt.async.Lwt.ignore_result pbehaves as follows:- If
pis already fulfilled,Lwt.ignore_result pdoes nothing. - If
pis already rejected withexn,Lwt.ignore_result praisesexnimmedaitely. - If
pis pending,Lwt.ignore_result pdoes nothing, but ifpbecomes rejected later, the exception is passed to!Lwt.async_exception_hook.
Use of this function is discouraged for two reasons:
- The behavior is different depending on whether
pis rejected now or later. - The name is misleading, and has led to users thinking this function is analogous to
Stdlib.ignore, i.e. that it waits forpto become resolved, completing any associated side effects along the way. In fact, the function that does that is ordinaryLwt.bind.
- If