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

**URL:** <https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972>\
**Category:** Ecosystem\
**Tags:** monad, lwt, effects\
**Created:** [July 15, 2025, 9:50am UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972 "2025-07-15T09:50:32Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![raphael-proust](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/raphael-proust/32/415_2.png) [@raphael-proust](https://discuss.ocaml.org/u/raphael-proust)\
**Post date:** [July 15, 2025, 9:50am UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/1 "2025-07-15T09:50:32Z")

</div>

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:

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

```

and allows you to write code such as

```ocaml
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](https://github.com/ocsigen/lwt/blob/6.0.0-alpha00/src/direct/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](https://github.com/ocsigen/lwt/pull/1058))

### Installation

`lwt.6.0.0~alpha00` and `lwt_direct.6.0.0~alpha00` will soon be released on opam ([PR on opam-repo](https://github.com/ocaml/opam-repository/pull/28175). 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:

```bash
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.

---

<div class="post-metadata">

**Author:** ![R\_Huxton](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/r_huxton/32/4216_2.png) [@R\_Huxton](https://discuss.ocaml.org/u/R_Huxton)\
**Post date:** [July 15, 2025, 1:29pm UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/2 "2025-07-15T13:29:25Z")

</div>

> [@raphael-proust](#):
>
> ```auto
> 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)
> 
> ```

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

---

<div class="post-metadata">

**Author:** ![c-cube](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/c-cube/32/1727_2.png) [@c-cube](https://discuss.ocaml.org/u/c-cube)\
**Post date:** [July 15, 2025, 1:36pm UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/3 "2025-07-15T13:36:48Z")

</div>

No, it’s `!continue`, to read the reference 🙂 . 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.

---

<div class="post-metadata">

**Author:** ![raphael-proust](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/raphael-proust/32/415_2.png) [@raphael-proust](https://discuss.ocaml.org/u/raphael-proust)\
**Post date:** [July 15, 2025, 1:40pm UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/4 "2025-07-15T13:40:24Z")

</div>

As @c-cube mentions, it’s to read the reference. See [OCaml library : Stdlib](https://ocaml.org/manual/5.3/api/Stdlib.html#VAL(!)) for official documentation on `!` especially and [OCaml - The core language](https://ocaml.org/manual/5.3/coreexamples.html#s:imperative-features) for general info on imperative programming in OCaml.

---

<div class="post-metadata">

**Author:** ![R\_Huxton](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/r_huxton/32/4216_2.png) [@R\_Huxton](https://discuss.ocaml.org/u/R_Huxton)\
**Post date:** [July 15, 2025, 4:14pm UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/5 "2025-07-15T16:14:46Z")

</div>

> [@c-cube](#):
>
> No, it’s `!continue`, to read the reference

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

---

<div class="post-metadata">

**Author:** ![vrotaru](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/vrotaru/32/39_2.png) [@vrotaru](https://discuss.ocaml.org/u/vrotaru)\
**Post date:** [July 15, 2025, 5:52pm UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/6 "2025-07-15T17:52:29Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)\
**Post date:** [July 15, 2025, 8:50pm UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/7 "2025-07-15T20:50:22Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![c-cube](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/c-cube/32/1727_2.png) [@c-cube](https://discuss.ocaml.org/u/c-cube)\
**Post date:** [July 16, 2025, 2:35am UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/8 "2025-07-16T02:35:45Z")

</div>

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 🙂

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?

---

<div class="post-metadata">

**Author:** ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)\
**Post date:** [July 16, 2025, 2:47am UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/9 "2025-07-16T02:47:24Z")

</div>

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](https://zio.dev/zio-direct/#introduction)

`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.

---

<div class="post-metadata">

**Author:** ![c-cube](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/c-cube/32/1727_2.png) [@c-cube](https://discuss.ocaml.org/u/c-cube)\
**Post date:** [July 16, 2025, 2:51am UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/10 "2025-07-16T02:51:28Z")

</div>

> [@yawaramin](#):
>
> Oh OK, so the question is should Lwt 6 keep supporting OCaml 4?

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!

---

<div class="post-metadata">

**Author:** ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)\
**Post date:** [July 16, 2025, 2:54am UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/11 "2025-07-16T02:54:56Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)\
**Post date:** [July 16, 2025, 3:02am UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/12 "2025-07-16T03:02:59Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![smondet](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/smondet/32/111_2.png) [@smondet](https://discuss.ocaml.org/u/smondet)\
**Post date:** [July 16, 2025, 3:08am UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/13 "2025-07-16T03:08:57Z")

</div>

> [@yawaramin](#):
>
> Why not in the main `Lwt` module itself?

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?).

---

<div class="post-metadata">

**Author:** ![c-cube](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/c-cube/32/1727_2.png) [@c-cube](https://discuss.ocaml.org/u/c-cube)\
**Post date:** [July 16, 2025, 3:24am UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/14 "2025-07-16T03:24:02Z")

</div>

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!

---

<div class="post-metadata">

**Author:** ![raphael-proust](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/raphael-proust/32/415_2.png) [@raphael-proust](https://discuss.ocaml.org/u/raphael-proust)\
**Post date:** [July 16, 2025, 6:23am UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/15 "2025-07-16T06:23:19Z")

</div>

Yes, as mentioned by @c-cube the hope is to keep lwt compatible with ocaml4. It’ll likely require some more work on the multi-domain-multi-scheduler feature but I think it should be doable.

As for names, I think a separate module is nice, I can see `Lwt_direct.run` being renamed `Lwt_direct.direct`, or maybe the introduction of `Lwt_direct.Syntax` module which you are meant to open and which provides `direct` and `await` and `yield` so you write `let open Lwt_direct.Syntax in direct (… bunch of code with awaits …)`. Suggestions definitelly welcome.

---

<div class="post-metadata">

**Author:** ![raphael-proust](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/raphael-proust/32/415_2.png) [@raphael-proust](https://discuss.ocaml.org/u/raphael-proust)\
**Post date:** [July 16, 2025, 6:24am UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/16 "2025-07-16T06:24:20Z")

</div>

Oh kind of off-topic but is there a summary somewhere about how it’s done? Also, does it use generators at all?

---

<div class="post-metadata">

**Author:** ![cvine](https://avatars.discourse-cdn.com/v4/letter/c/90db22/32.png) [@cvine](https://discuss.ocaml.org/u/cvine)\
**Post date:** [July 16, 2025, 12:13pm UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/17 "2025-07-16T12:13:42Z")

</div>

> [@raphael-proust](#):
>
> ### Feedback
> 
> Don’t hesitate to chime in on here with any feedback you may have. Ideas, comments, requests, suggestions, etc.

Is the release of Lwt-6 a suitable opportunity to drop the `Lwt_process` module, as I don’t think it gives `Lwt` a good look?

That is because that module can no longer be safely used, as `Lwt` implicitly starts `Thread.t` threads when encountering blocking calls, and `Lwt_unix.set_default_async_method` is no longer able to change this in recent versions of `Lwt`. The problem is that `Lwt_process` calls up `Unix.fork` (via `Lwt_unix.fork`) and also `Unix.exec*`, none of which is thread safe.

About `Unix.fork` the OCaml documentation says “`[Unix.fork]` fails if the OCaml process is multi-core (any domain has been spawned). In addition, if any thread from the `Thread` module has been spawned, then the child process might be in a corrupted state.”

About the `Unix.exec*` functions, those can allocate memory with malloc and so are not async-signal-safe and so not thread/domain safe ([Unix.create\_process\_env might not be multi-thread safe · Issue #12395 · ocaml/ocaml · GitHub](https://github.com/ocaml/ocaml/issues/12395)).

There are also some things in `Lwt_unix` which are unsafe and could be removed for similar reasons, such as `Lwt_unix.fork` and `Lwt_unix.system`.

---

<div class="post-metadata">

**Author:** ![lindig](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/lindig/32/532_2.png) [@lindig](https://discuss.ocaml.org/u/lindig)\
**Post date:** [July 16, 2025, 12:20pm UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/18 "2025-07-16T12:20:04Z")

</div>

> [@raphael-proust](#):
>
> Don’t hesitate to chime in on here with any feedback you may have. Ideas, comments, requests, suggestions, etc.

To build an intuition, I would welcome an explanation what `run` and `await` do; also, it seems this can’t be implemented with OCaml 4 - but why is that?

---

<div class="post-metadata">

**Author:** ![raphael-proust](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/raphael-proust/32/415_2.png) [@raphael-proust](https://discuss.ocaml.org/u/raphael-proust)\
**Post date:** [July 16, 2025, 12:33pm UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/19 "2025-07-16T12:33:11Z")

</div>

I’ll check what uses of `Lwt_process` and `Lwt_unix.fork` there are in the ~~wild~~ opam-repository and consider that. Thanks for the suggestion!

---

<div class="post-metadata">

**Author:** ![raphael-proust](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/raphael-proust/32/415_2.png) [@raphael-proust](https://discuss.ocaml.org/u/raphael-proust)\
**Post date:** [July 16, 2025, 12:42pm UTC](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972/20 "2025-07-16T12:42:34Z")

</div>

The gist of it is:

`run : (unit -> 'a) -> 'a Lwt.t` creates a promise, sets up an effect handler to support `await` during the execution of its argument, executes its argument, resolves the promise with the result that is returned.

`await : 'a Lwt.t -> 'a` takes a promise as argument. Because of the way the Lwt promises work, that promise’s evaluation is either a resolved promise or a promise waiting for the scheduler to be awoken (when IO is ready or at the next tick). In the former case `await` simply returns the value. In the latter case `await` performs an effect. Upon receiving this effect, the handler installed by `run` attaches the `continue` callback to the waiting promise (via a simple `Lwt.on_any`). More specifically, the handler installs a callback to the promise, the callback puts the `continue` inside a queue, the queue is iterated upon when the scheduler starts a new tick.

There is a bit more to it, like exceptions. But that’s the gist of it.

This is not possible in OCaml 4 because you can’t punch through the scheduler, do some side-effects, and return to wherever you happened to be at.

[Next page](https://discuss.ocaml.org/t/ann-lwt-6-0-0-alpha-direct-style/16972.md?page=2)
