8.24 Binding operators

let-operator::=
let ($∣ &∣ ∣ +∣ -∣ /∣ <∣ =∣ >∣ @∣ ^∣ |) { operator-char }
and-operator::=
and ($∣ &∣ ∣ +∣ -∣ /∣ <∣ =∣ >∣ @∣ ^∣ |) { operator-char }
operator-name ::=
let-operator
and-operator
expr::=
let-operator let-binding { and-operator let-binding } in expr

Users can define let operators:

  1. let ( let* ) o f =
  2. match o with
  3. | None -> None
  4. | Some x -> f x
  5. let return x = Some x
  6. val ( let* ) : 'a option -> ('a -> 'b option) -> 'b option = <fun>
  7. val return : 'a -> 'a option = <fun>

and then apply them using this convenient syntax:

  1. let find_and_sum tbl k1 k2 =
  2. let* x1 = Hashtbl.find_opt tbl k1 in
  3. let* x2 = Hashtbl.find_opt tbl k2 in
  4. return (x1 + x2)
  5. val find_and_sum : ('a, int) Hashtbl.t -> 'a -> 'a -> int option = <fun>

which is equivalent to this expanded form:

  1. let find_and_sum tbl k1 k2 =
  2. ( let* ) (Hashtbl.find_opt tbl k1)
  3. (fun x1 ->
  4. ( let* ) (Hashtbl.find_opt tbl k2)
  5. (fun x2 -> return (x1 + x2)))
  6. val find_and_sum : ('a, int) Hashtbl.t -> 'a -> 'a -> int option = <fun>

Users can also define and operators:

  1. module ZipSeq = struct
  2. type 'a t = 'a Seq.t
  3. open Seq
  4. let rec return x =
  5. fun () -> Cons(x, return x)
  6. let rec prod a b =
  7. fun () ->
  8. match a (), b () with
  9. | Nil, _ | _, Nil -> Nil
  10. | Cons(x, a), Cons(y, b) -> Cons((x, y), prod a b)
  11. let ( let+ ) f s = map s f
  12. let ( and+ ) a b = prod a b
  13. end
  14. module ZipSeq :
  15. sig
  16. type 'a t = 'a Seq.t
  17. val return : 'a -> 'a Seq.t
  18. val prod : 'a Seq.t -> 'b Seq.t -> ('a * 'b) Seq.t
  19. val ( let+ ) : 'a Seq.t -> ('a -> 'b) -> 'b Seq.t
  20. val ( and+ ) : 'a Seq.t -> 'b Seq.t -> ('a * 'b) Seq.t
  21. end

to support the syntax:

  1. open ZipSeq
  2. let sum3 z1 z2 z3 =
  3. let+ x1 = z1
  4. and+ x2 = z2
  5. and+ x3 = z3 in
  6. x1 + x2 + x3
  7. val sum3 : int Seq.t -> int Seq.t -> int Seq.t -> int Seq.t = <fun>

which is equivalent to this expanded form:

  1. open ZipSeq
  2. let sum3 z1 z2 z3 =
  3. ( let+ ) (( and+ ) (( and+ ) z1 z2) z3)
  4. (fun ((x1, x2), x3) -> x1 + x2 + x3)
  5. val sum3 : int Seq.t -> int Seq.t -> int Seq.t -> int Seq.t = <fun>

8.24.1 Rationale

This extension is intended to provide a convenient syntax for workingwith monads and applicatives.

An applicative should provide a module implementing the followinginterface:

  1. module type Applicative_syntax = sig
  2. type 'a t
  3. val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t
  4. val ( and+ ): 'a t -> 'b t -> ('a * 'b) t
  5. end
  6.  

where (let+) is bound to the map operation and (and+) is bound tothe monoidal product operation.

A monad should provide a module implementing the following interface:

  1. module type Monad_syntax = sig
  2. include Applicative_syntax
  3. val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t
  4. val ( and* ): 'a t -> 'b t -> ('a * 'b) t
  5. end
  6.  

where (let) is bound to the bind operation, and (and) is alsobound to the monoidal product operation.