Let Expressions
In our use of the word let
thus far, we've been making definitions
in the toplevel and in .ml
files. For example,
# let x = 42;;
val x : int = 42
defines x
to be 42, after which we can use x
in future definitions
at the toplevel. We'll call this use of let
a let definition.
There's another use of let
which is as an expression:
# let x = 42 in x+1
- : int = 43
Here we're binding a value to the name x
then using that binding
inside another expression, x+1
. We'll call this use of let
a
let expression. Since it's an expression it evaluates to a value.
That's different than definitions, which themselves do not evaluate
to any value. You can see that if you try putting a let definition
in place of where an expression is expected:
# (let x = 42) + 1
Error: Syntax error: operator expected.
Syntactically, a let
definition is not permitted on the left-hand side
of the +
operator, because a value is needed there, and definitions
do not evaluate to values. On the other hand, a let
expression
would work fine:
# (let x = 42 in x) + 1
- : int = 43
Another way to understand let definitions at the toplevel is that they are like let expression where we just haven't provided the body expression yet. Implicitly, that body expression is whatever else we type in the future. For example,
# let a = "big";;
# let b = "red";;
# let c = a^b;;
# ...
is understand by OCaml in the same way as
let a = "big" in
let b = "red" in
let c = a^b in
...
That latter series of let
bindings is idiomatically how several variables
can be bound inside a given block of code.
Syntax.
let x = e1 in e2
As usual, x
is an identifier. We call e1
the binding expression, because
it's what's being bound to x
; and we call e2
the body expression,
because that's the body of code in which the binding will be in scope.
Dynamic semantics.
To evaluate let x = e1 in e2
:
Evaluate
e1
to a valuev1
.Substitute
v1
forx
ine2
, yielding a new expressione2'
.Evaluate
e2'
to a valuev2
.The result of evaluating the let expression is
v2
.
Here's an example:
let x = 1+4 in x*3
--> (evaluate e1 to a value v1)
let x = 5 in x*3
--> (substitute v1 for x in e2, yielding e2')
5*3
--> (evaluate e2' to v2)
15
(result of evaluation is v2)
Static semantics.
- If
e1:t1
and if under the assumption thatx:t1
it holds thate2:t2
, then(let x = e1 in e2) : t2
.
We use the parentheses above just for clarity. As usual, the compiler's type inferencer determines what the type of the variable is, or the programmer could explicitly annotate it with this syntax:
let x : t = e1 in e2