## 8.24 Binding operators

- 8.24.1 Rationale

(Introduced in 4.08.0)

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:

     let ( let* ) o f =
       match o with
       | None -> None
       | Some x -> f x

     let return x = Some x
    val ( let* ) : 'a option -> ('a -> 'b option) -> 'b option = <fun>
    val return : 'a -> 'a option = <fun>

and then apply them using this convenient syntax:

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

which is equivalent to this expanded form:

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

Users can also define and operators:

     module ZipSeq = struct

       type 'a t = 'a Seq.t

       open Seq

       let rec return x =
         fun () -> Cons(x, return x)

       let rec prod a b =
         fun () ->
           match a (), b () with
           | Nil, _ | _, Nil -> Nil
           | Cons(x, a), Cons(y, b) -> Cons((x, y), prod a b)

       let ( let+ ) f s = map s f
       let ( and+ ) a b = prod a b

     end
    module ZipSeq :
      sig
        type 'a t = 'a Seq.t
        val return : 'a -> 'a Seq.t
        val prod : 'a Seq.t -> 'b Seq.t -> ('a * 'b) Seq.t
        val ( let+ ) : 'a Seq.t -> ('a -> 'b) -> 'b Seq.t
        val ( and+ ) : 'a Seq.t -> 'b Seq.t -> ('a * 'b) Seq.t
      end

to support the syntax:

     open ZipSeq
     let sum3 z1 z2 z3 =
       let+ x1 = z1
       and+ x2 = z2
       and+ x3 = z3 in
         x1 + x2 + x3
    val sum3 : int Seq.t -> int Seq.t -> int Seq.t -> int Seq.t = <fun>

which is equivalent to this expanded form:

     open ZipSeq
     let sum3 z1 z2 z3 =
       ( let+ ) (( and+ ) (( and+ ) z1 z2) z3)
         (fun ((x1, x2), x3) -> x1 + x2 + x3)
    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 working with monads and applicatives.

An applicative should provide a module implementing the following interface:

     module type Applicative_syntax = sig
       type 'a t
       val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t
       val ( and+ ): 'a t -> 'b t -> ('a * 'b) t
     end

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

A monad should provide a module implementing the following interface:

     module type Monad_syntax = sig
       include Applicative_syntax
       val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t
       val ( and* ): 'a t -> 'b t -> ('a * 'b) t
     end

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