Monad, Rust's ?, result, ocaml, let

I’m looking for something similar to Rust’s? operator on functions that return Result<T, E> (in particular, it shortcircuits on first error).

I found: OCaml library : Result , which has (1) the type I need and (2) the bind operator I need.

I also see let* from OCaml - Language extensions .

What I can’t figure out is: how do we put this together for functions that return an ('a, 'e) result ?

Does this help: Provide let operators in the standard library by lpw25 · Pull Request #2170 · ocaml/ocaml · GitHub

I don’t understand what I am supposed to do with this. Is there a minimal repo with a dune file + a *.ml file using monadic result ?

I think it’s just meant to give you a code snippet example, specifically this should just work for whatever bind, product, map are:

let return x = Ok x

let ( let* ) x f = bind x f

let ( and* ) a b = product a b

let ( let+ ) x f = map f x

let ( and+ ) a b = product a b

And further down there are some examples:

(* Option *)

let option_map =
  Option.Syntax.(
    let+ x = Some 4 in
    x + 1
  );;
[%%expect{|
val option_map : int option = Some 5
|}];;

let option_map2 =
  Option.Syntax.(
    let+ x = None in
    x + 1
  );;
[%%expect{|
val option_map2 : int option = None
|}];;

let option_map_and =
  Option.Syntax.(
    let+ x = Some 5
    and+ y = Some 8 in
    x + y
  );;
[%%expect{|
val option_map_and : int option = Some 13
|}];;

let option_map_and2 =
  Option.Syntax.(
    let+ x = None
    and+ y = Some 8 in
    x + y
  );;
[%%expect{|
val option_map_and2 : int option = None
|}];;

let option_map_and3 =
  Option.Syntax.(
    let+ x = Some 5
    and+ y = None in
    x + y
  );;
[%%expect{|
val option_map_and3 : int option = None
|}];;

let option_bind =
  Option.Syntax.(
    let* x = Some 5 in
    let* y = Some 6 in
    return (x + y)
  );;
[%%expect{|
val option_bind : int option = Some 11
|}];;

let option_bind2 =
  Option.Syntax.(
    let* x = None in
    let* y = Some 7 in
    return (x + y)
  );;
[%%expect{|
val option_bind2 : int option = None
|}];;

let option_bind3 =
  Option.Syntax.(
    let* x = Some 3 in
    let* y = None in
    return (x + y)
  );;
[%%expect{|
val option_bind3 : int option = None
|}];;

let option_bind_and =
  Option.Syntax.(
    let* x = Some 2
    and* y = Some 7 in
    return (x + y)
  );;
[%%expect{|
val option_bind_and : int option = Some 9
|}];;

let option_bind_and2 =
  Option.Syntax.(
    let* x = None
    and* y = Some 9 in
    return (x + y)
  );;
[%%expect{|
val option_bind_and2 : int option = None
|}];;

let option_bind_and3 =
  Option.Syntax.(
    let* x = Some 3
    and* y = None in
    return (x + y)
  );;
[%%expect{|
val option_bind_and3 : int option = None
|}];;

let option_bind_map =
  Option.Syntax.(
    let* x = Some 3 in
    let+ y = Some 7 in
    x + y
  );;
[%%expect{|
val option_bind_map : int option = Some 10
|}];;

Disclaimer: see the license file in the repo of the PR.

This problem is related to the concept of a monad (which I still haven’t fully understood or studied yet)

Maybe this beginner oriented example will help you understand more: multi-playground/monads.ml at master · benjamin-thomas/multi-playground · GitHub

In short, you don’t need an early return at all.

You can apply this same concept to the result type

There’s not much you need to do. Just issue a

let ( let* ) = Result.bind

at the toplevel of your module. You can now use let* to unpack the Ok case of result values:

let read_int : () -> (int, string) result = … 

let read_and_add () : (int, string) result = 
  let* i = read_int () in 
  Ok (i + 1)
4 Likes