How to read this function signature from the Caqti codebase?


let (>>=?) m f =
  m >>= (function | Ok x -> f x | Error err -> Lwt.return (Error err))

How do I read this? I understand what’s happening in use conceptually, but it’s hard to read.

My understanding: “hey if we got a left/right, good/bad result pipeline. If you got functions that follow this signature, you can pipe this along with other functions of similar signature”

I’m not familiar with the common fp patterns, and am still reading some Haskell stuff.

Your understanding is fundamentally correct.

There are two monads in flight here: the Result monad (Ok/Error), and the Lwt monad that encapsulates its concurrency semantics. It’s common in Caqti to be working with results held within Lwt (i.e. ('a, 'err) Result.t Lwt.t), and so it is useful (using something like this >>=?? helper in this example) to be able to thread binding functions down to those inner results without explicitly matching on them (just the same as you’d use Result’s >>= on bare result values).

If you didn’t use an operator like >>=??, the test example function in the sample you linked would have to look something like:

let test db =
  create_bikereg db
  >>= (function Ok () -> reg_bike db "BIKE-0000" "Arthur Dent" | (Error _) as err -> Lwt.return err)
  >>= (function Ok () -> reg_bike db "BIKE-0001" "Ford Prefect" | (Error _) as err -> Lwt.return err)

…and so on, using the bind (>>=) operator defined by Lwt.

In Haskell, this nested relationship (and useful composed operations around it, like what >>=?? does) would be formalized using a monad transformer. There is a library providing such functionality in OCaml (@ivg et al.‘s excellent monads library), but such patterns aren’t commonly applied (unfortunately so IMO, but ofc, others’ opinions vary :slight_smile:).


Sorry, first I’ll be a little pendantic about terminology :slight_smile:

That is not a function signature, it’s an implementation. Anyway, this is the exact same thing as Lwt_result.bind . The Lwt_result module has a bunch of helpers like that for dealing with ('a, 'e) result Lwt.t values. This includes new let-operators, which I personally prefer. E.g. the first function after that definition could look like:

let test db =
  let open Lwt_result.Syntax in
  (* Examples of statement execution: Create and populate the register. *)
  let* () = create_bikereg db in
  let* () = reg_bike db "BIKE-0000" "Arthur Dent" in
  let* () = reg_bike db "BIKE-0001" "Ford Prefect" in
1 Like

Maybe it would help you to mentally rewrite

let (>>=?) m f =
  m >>= (function | Ok x -> f x | Error err -> Lwt.return (Error err))


let ( >>=? ) m f =
  >>= fun v ->
  match v with
  | Ok x -> f x
  | Error err -> Lwt.return (Error err)
1 Like

Hah, I’d never noticed Lwt_result.Infix, thanks! (Although, ech, another set of operator clashes, too bad about modular implicits, etc. :slight_smile:)

1 Like

Yeah, I’m starting to come around to the viewpoint that modular implicits would be better than carefully managing local/global opens to bring operators into scope at the right places.

Yes, using the new let-operators looks better, though I need to keep backwards compatibility with the testsuite for a bit longer. I only discovered Lwt_result.Syntax now. I started using (let*?) and (let+?) in other projects, and I find it useful to mix the Lwt.t and the Lwt_result.t monads in the same function. Not sure if I’ve chosen the most natural operator names, though, could also have used a ! suffix or something else.

I agree modular implicits would be great to have for such cases, but given the current state, OCaml has precedence for avoiding operator clashes for integers and floats using .-suffixes. I think we can do the same in the meantime, as long as there are only two monads of interest.

Edit: Make that three, including the plain Result.t monad.

1 Like

Thank you everyone.

The rewrite from the custom operator to the more common let bind and bind operators was really helpful. I’ll have to sit on this and use it more often :slight_smile:

1 Like