[ANN] Lwt.6.0.0~alpha (direct-style)

It is a great pleasure to announce the release of the first alpha release of Lwt 6. This major version bump brings two major changes to Lwt:

  • Using Lwt in direct-style! (Big thanks to @c-cube !!)
  • Using multiple Lwt schedulers running in separate domains!

Direct-style

This contribution from @c-cube is available in alpha00. It comes in the form of an lwt_direct package which provide an Lwt_direct module which provide two core functions:

val run : (unit -> 'a) -> 'a Lwt.t
val await : 'a Lwt.t -> 'a

and allows you to write code such as

run (fun () ->
  let continue = ref true in
  while !continue do
    match await @@ Lwt_io.read_line ic with
    | line -> await @@ Lwt_io.write_line oc line
    | exception End_of_file -> continue := false
  done)

There are a few more functions. All of which is documented in lwt_direct.mli.

Multi-scheduler

This addition is not available in alpha00 but should be added to alpha01 soon. It allows to call Lwt_main.run in different domains and benefit from actual parallelism. (Sneak peek in this pull request)

Installation

lwt.6.0.0~alpha00 and lwt_direct.6.0.0~alpha00 will soon be released on opam (PR on opam-repo. I’ll publish some more alphas as the work progresses, and announce the releases on this thread.

You can also pin the packages to the lwt-6 branch to get everything a little bit earlier:

opam pin lwt https://github.com/ocsigen/lwt.git#lwt-6
opam pin lwt_direct https://github.com/ocsigen/lwt.git#lwt-6

Feedback

Don’t hesitate to chime in on here with any feedback you may have. Ideas, comments, requests, suggestions, etc.

14 Likes

I’m guessing this should be while continue do, but it looks like an attractive option.

No, it’s !continue, to read the reference :slight_smile: . There are multiple ways to write this loop, but here it emphasizes the (new) possibility of writing direct style code that runs on Lwt’s scheduler and can wait on any Lwt.t promise.

2 Likes

As @c-cube mentions, it’s to read the reference. See OCaml library : Stdlib for official documentation on ! especially and OCaml - The core language for general info on imperative programming in OCaml.

2 Likes

D’oh! The perils of working in different languages all the time. I still can’t read it as anything other than “while not” even now - the habit is too deeply ingrained. It’s switching between semicolon-terminated and not that mostly gets me for the first hour or two.

Thanks

I’m reminded of someone humorous explanations which may help with the correct interpretation.

How do you get a value out of a reference?
You bang(!) it.

Question: why in a separate Lwt_direct module? Why not in the main Lwt module itself? Is it because of the name run? Could we use Lwt.defer and Lwt.now instead?

In the current state of things, lwt_direct requires OCaml 5 while lwt doesn’t. I think otherwise it could make sense for Lwt_direct to live in the lwt library, just maybe not in the Lwt module itself — after all it’s not about promises as much as the moral equivalent of microtasks.

It’s an add-on to lwt offering a different way of thinking, while keeping full compatibility with the ecosystem. A separate module seems reasonable to me :slight_smile:

I’m not sure why you would pick names like defer and now. defer sounds like a resource handling function, and now like a clock function?

Oh OK, so the question is should Lwt 6 keep supporting OCaml 4?

defer is used in ZIO-Direct, which is an equivalent feature in Scala’s ZIO effect system: Introduction to ZIO Direct Style | ZIO

now is just a contrast to ‘defer’, as in ‘get the value later’ vs ‘get the value now’. ‘Defer’ terminology is also used by Jane Street Async–their promise type is 'a Deferred.t.

Maybe async and await would actually be better choices, but there is already Lwt.async so it might be confusing.

I think it’s a good question, since @raphael-proust has in the works a multicore PR (one Lwt event loop per domain). Even then I think a separate module would be good, inside the lwt library.

Thanks for explaining defer and now. I think await is non-negotiable, it’s almost universal at this point. run is a bit generic and I could imagine there being better names!

1 Like

Yeah, I can imagine there being confusion about Lwt_main.run vs Lwt_direct.run.

One more question: does try await promise with ex -> ... do the equivalent of Lwt.catch ie handle promise rejection?

May be worth mentioning, the Lwt module is available and very useful in the js-of-ocaml world where effects-compilation is even younger (even kinda experimental?).

2 Likes

Yes, Lwt_direct.await promise will raise the exception e if promise failed with e. It all works normally, try … with, match … with exception …, awaiting inside List.map, etc. Effects are awesome!

1 Like