ReasonML: let bindings and scopes

[2017-12-11] dev, reasonml
(Ad, please don’t block)

Table of contents for this series of posts: “What is ReasonML?


In this blog post, we look at how to introduce new variables and scopes in ReasonML.

Normal let bindings  

Variables are defined as follows:

# let x = 123;
let x: int = 123;
# x;
- : int = 123

Each binding (variable-value pair) that is created in this manner is immutable – you cannot assign a different value to the variable. The norm is for the value to also be immutable, but it doesn’t have to be.

Given that the binding is immutable, it is logical that you have to immediately initialize the variable. You can’t leave it uninitialized.

Redefining variables  

ReasonML does not prevent you from redefining variables:

# let x = 1;
let x: int = 1;
# x;
- : int = 1
# let x = 2;
let x: int = 2;
# x;
- : int = 2

This is not in conflict with the immutability of bindings: It works more like shadowing in nested scopes than like changing the value of a variable.

Being able to redefine variables is especially useful in interactive command lines.

Type annotations  

You can also annotate the variable with a type:

# let y: string = "abc";
let y: string = "abc";

Declaring types is occasionally necessary with more complicated types, but redundant with simple types.

Creating new scopes via scope blocks  

The scope of a variable is the syntactic construct in which it exists. Blocks enable you introduce new scopes. They start and end with curly braces ({}):

let x = "hello";
print_string(x); /* hello */
{ /* A */
    let x = "tmp";
    print_string(x); /* tmp */
};  /* B */
print_string(x); /* hello */

The block starts in line A and ends in line B.

The interior of a block has the same structure as the top level of a file: it is a sequence of expressions that are separated by semicolons.

Why is there a semicolon after the closing curly brace in line B? A block is just another expression. Its value is the value of the last expression inside it. That means you can put code blocks wherever you can put expressions:

let x = { print_string("hi"); 123 }; /* hi */
print_int(x); /* 123 */

Another example:

print_string({
    let s = "ma";
    s ++ s;
}); /* mama */

This continues a common theme in ReasonML: most things are expressions.