The basic form of let expressions is:
let p1 = e1
and p2 = e2
...
and pN = eN
in e
where N
is at least 1. In this form, let expressions pattern matches the value that results from evaluating the RHS expressions against the LHS patterns, then evaluates the body with the new bindings defined by the LHS patterns in scope. For example,
let x, y = 1, 2 in
x + y
evaluates to 3.
When let
has an operator name attached, it is the application of what is called a "let operator" or "binding operator" (to give you easier terms to search up). For example:
let+ x, y = 1, 2 in
x + y
desugars to (let+) (1, 2) (fun (x, y) -> x + y)
. (Similar to how one surrounds the operator +
in parentheses, making it (+)
, to refer to its identifier, the identifier for the let operator let+
, as it appears in a let expression, would be (let+)
.)
Finally, when a let
binding has an operator name attached, all the and
bindings must have operator names attached as well.
let* x = 1
and+ y = 2
and* z = 3 in
x + y + z
desugars to (let*) ((and+) 1 ((and*) 2 3)) (fun ((x, y), z) ->)
.
The following program is invalid and has no meaning because the let
binding is being used as an operator, but the and
binding is not:
let* x = 1
and y = 2 in
x + y
Binding operators are covered in the "language extensions" section of the OCaml documentation.
let () = e
is merely the non-operator form of a pattern match, where ()
is the pattern that matches the only value of the unit
type. The unit
type is conventionally the type of expressions that don't evaluate to a meaningful value, but exist for side effects (e.g. print_endline "Hello world!"
). Matching against ()
ensures that the expression has type ()
, catching partial application errors. The following typechecks:
let f x y =
print_endline x;
print_endline y
let () =
f "Hello" "World"
The following does not:
let f x y =
print_endline x;
print_endline y
let () =
f "Hello" (* Missing second argument, so expression has type string -> unit, not unit *)
Note that the binding operators are useful for conveniently using "monads" and "applicatives," so you may hear these words when learning about binding operators. However, binding operators are not inherently related to these concepts. All they do is desugar to the expressions that I describe above, and any other significance (such as relation to monads) results from how the operator was defined.
let*
andlet+
are syntactic sugar for Monads and Applicatives. Similar to Haskellsdo
notation. – Spinnaker