Lwt in 5 minutes

Principles

The Lwt library implements cooperative threads for OCaml. Cooperative threads are an alternative to preemptive threads (used in many languages and in OCaml's Thread module) that solve most common issues with preemptive threads: with Lwt, there is very limited risk of deadlocks and very limited need for locks. Lwt threads are even usable in programs compiled to JavaScript using Js_of_ocaml.

Lwt is based on the fact that most programs spend most of their time waiting for inputs, e.g., keys, data coming from sockets, and mouse events. Instead of relying on a preemptive scheduler that switches between threads at arbitrary moments, Lwt uses these waiting times as cooperation points. This means that instead of blocking (for example on a read), Lwt resumes another waiting thread, if there is one that is ready to continue. All you have to do is to use the cooperative version of each blocking function, for example Lwt_unix.sleep instead of Unix.sleep and Lwt_unix.read instead of Unix.read. If one of your computations takes a lot of time, it is also possible to manually insert cooperation points using the function Lwt_main.yield.

Promises

Lwt defines a type 'a Lwt.t, which is the type of promises. For example, the function:

val f : unit -> int Lwt.t

immediately returns a promise of int, that is, something that will eventually become an integer once the computation is finished.

The following code will launch the computation of f () (asynchronously). If the code reaches a cooperation point (for example when the integer is requested via the network), it will continue the program (print "hello") and resume at a subsequent cooperation point, when the data is available.

let g1 () =
  let p = f () in
  print_endline "hello"

Bind: Using the value of promises

It is possible to tell Lwt to execute a function once a promise is completed, by using the function:

Lwt.bind : 'a Lwt.t -> ('a -> 'b Lwt.t) -> 'b Lwt.t

For instance Lwt.bind p h will call the function h with the return value of the promise p as soon as the value is known. The expression (Lwt.bind p h) is also a promise (it may take time to complete). Function h must return a promise.

To create a (terminated) promise from a value, use Lwt.return.

Example:

let g2 () =
  let p = f () in
  Lwt.bind p (fun i -> print_int i; Lwt.return ())

Function g2 calls function f to create a promise. Then it waits (in a cooperative manner) for the result, and prints the result. The expression g2 () has type unit Lwt.t.

Syntax extension

A PPX (and also camlp4) syntax extension is available.

let%lwt i = f () in
...

is equivalent to

Lwt.bind (f ()) (fun i -> ...)

Examples

A function that prints "tic" every second forever, without blocking the rest of the program

let rec tic () =
    print_endline "tic";
    let%lwt () = Lwt_unix.sleep 1.0 in
    tic ()

Replace Lwt_unix.sleep by Lwt_js.sleep if your program is running in a browser.

Launching concurrent threads and waiting for their results

Suppose you have two cooperative functions:

val f : unit -> unit Lwt.t
val g : unit -> unit Lwt.t

The following code runs f () and g () sequentially:

let%lwt () = f () in
let%lwt () = g () in
...

The following code launches f () and g () concurrently, then waits for both to terminate before continuing:

let p1 = f () in
let p2 = g () in
let%lwt () = p1 in
let%lwt () = p2 in
...

To detach a thread, it is recommended to use

Lwt.async (fun () -> f ())

instead of

ignore (f ())

to catch exceptions properly.

Serial and concurrent map on lists

The following map function runs all computation concurrently on all list elements:

let rec map f l =
  match l with
  | [] -> Lwt.return []
  | v :: r ->
      let t = f v in
      let rt = map f r in
      let%lwt v' = t in
      let%lwt l' = rt in
      Lwt.return (v' :: l')

whereas the following one waits for the one to complete before launching the next one:

let rec map_serial f l =
  match l with
  | [] -> return []
  | v :: r ->
      let%lwt v' = f v in
      let%lwt l' = map_serial f r in
      Lwt.return (v' :: l')

More documentation

Have a look at Lwt's manual for more details about Lwt. You will learn how to handle exceptions (using Lwt.fail and Lwt.catch or try%lwt). You will also learn for example how to create a thread that waits until it is awaken (using Lwt.wait and Lwt.wakeup).