Lwt now has let* syntax

Lwt now has let* and let+ syntax, which can be used like this:


open Lwt.Syntax

let () =
  let request =
    let* addresses = Lwt_unix.getaddrinfo "google.com" "80" [] in
    let google = Lwt_unix.((List.hd addresses).ai_addr) in

    Lwt_io.(with_connection google (fun (incoming, outgoing) ->
      let* () = write outgoing "GET / HTTP/1.1\r\n" in
      let* () = write outgoing "Connection: close\r\n\r\n" in
      let* response = read incoming in
      Lwt.return (Some response)))
  in

  let timeout =
    let* () = Lwt_unix.sleep 5. in
    Lwt.return None
  in

  match Lwt_main.run (Lwt.pick [request; timeout]) with
  | Some response -> print_string response
  | None -> prerr_endline "Request timed out"; exit 1

This is now released in Lwt 5.3.0. Thanks to Rahul Kumar for adding let*, and @CraigFe for adding let+!

25 Likes

Awesome this looks great.

2 quick questions:

  1. I don’t see this new version documented on ocsigen yet? Is that a build that needs to be done manually?
  2. Is ppx_lwt still recommend for some usecases like try%? For what cases is one preferred over the other?
1 Like

Good questions :slight_smile:

  1. The docs generation is blocked on an Ocsigen “internal” package wikidoc, which has not been updated to support 4.08. So, effectively, let* is exactly what is preventing docs generation for the time being. I’ll post the docs as soon as that is fixed.
  2. ppx_lwt is probably still the recommended way, because of better backtraces, and things like try%lwt. let* is nice for people that don’t want to use the PPX. They can still benefit from a monadic syntax.
2 Likes

Thanks!

about 2, I’ve think I’ve read that already some place. Are the things like try* on the roadmap for ocaml? Or are those things that we probably won’t see?

1 Like

I’m not aware of any plans for try, but there is a PR for match here. There is some discussion of exception handling there.

1 Like

I presume there is no reason why you should not use let* in place of let%lwt if you are using ocaml >= 4.08, even if you are using ppx_lwt? After all, (let*) is presumably just an alias for Lwt.bind.

The let+ (mapping) operator is an interesting one. Is there a ppx_lwt equivalent for that? I don’t think I have ever come across a use for it if there is.

1 Like

let%lwt maps to Lwt.backtrace_bind and can do even more manipulation - see the ppx code for more details.

2 Likes

I guess there is no let%lwt for Lwt_result either, so let* can be useful there as well

1 Like

let%lwt should produce better backtraces, because only the PPX inserts a function at the call site that uses %reraise to extend the backtrace of an exception in the callback of the bind. This is what adds the location of the call site to the backtrace. The PPX actually uses backtrace_bind mentioned by @hcarty — that’s an internal version of bind with an extra callback argument, meant for that generated backtrace-extending function.

let* bypasses this completely and expands to ordinary bind. So, if the callback of bind ends up being called asynchronously (later), a backtrace generated by an exception raised at that point will lack the location of the call site, and will show only the backtrace to where the Lwt main loop was awakened, leading to the callback call. It won’t show where the bind actually happened.

Lwt really ought to have something better for this, but we never got around to it.

There are indeed no equivalents to let+ and Lwt_result.(let*) in the PPX.

6 Likes

Thank you for the extensive reply. Very interesting.

I use let ( let*) = Lwt_result.bind in... quite frequently! It’s very useful in that context, with the caveats @antron mentioned relative to using let%lwt.

1 Like