Docs / Language Manual / LetBinding

You are currently looking at the v8.0.0 docs (Reason v3.6 syntax edition). You can find the latest manual page here.

Let Binding

A "let binding", in other languages, might be called a "variable declaration". let binds values to names. They can be seen and referenced by code that comes after them.

Reason (Old Syntax)ML (Older Syntax)JS Output
let greeting = "hello!";
let score = 10;
let newScore = 10 + score;

Block Scope

Bindings can be scoped through {}.

Reason (Old Syntax)ML (Older Syntax)JS Output
let message = {
  let part1 = "hello";
  let part2 = "world";
  part1 ++ " " ++ part2;
// `part1` and `part2` not accessible here!

The value of the last line of a scope is implicitly returned.

Design Decisions

ReScript's if, while and functions all use the same block scoping mecanism. The code below works not because of some special "if scope"; but simply because it's the same scope syntax and feature you just saw:

Reason (Old Syntax)ML (Older Syntax)JS Output
if (displayGreeting) {
  let message = "Enjoying the docs so far?";
// `message` not accessible here!

Bindings Are Immutable

Let bindings are "immutable", aka "cannot change". This helps our type system deduce and optimize much more than other languages (and in turn, help you more).

Binding Shadowing

The above restriction might sound unpractical at first. How would you change a value then? Usually, 2 ways:

The first is to realize that many times, what you want isn't to mutate a variable's value. For example, this JavaScript pattern:

var result = 0; result = calculate(result); result = calculateSomeMore(result); really just to comment on intermediate steps. You didn't need to mutate result at all! You could have just written this JS:

var result1 = 0; var result2 = calculate(result1); var result3 = calculateSomeMore(result2);

In ReScript, this obviously works too:

Reason (Old Syntax)ML (Older Syntax)JS Output
let result1 = 0;
let result2 = calculate(result1);
let result3 = calculateSomeMore(result2);

Additionally, reusing the same let binding name overshadows the previous bindings with the same name. So you can write this too:

Reason (Old Syntax)ML (Older Syntax)JS Output
let result = 0;
let result = calculate(result);
let result = calculateSomeMore(result);

(Though for the sake of clarity, we don't recommend this).

As a matter of fact, even this is valid code:

Reason (Old Syntax)ML (Older Syntax)JS Output
let result = "hello";
Js.log(result); // prints "hello"
let result = 1;
Js.log(result); // prints 1

The binding you refer to is whatever's the closest upward. No mutation here! If you need real mutation, e.g. passing a value around, have it modified by many pieces of code, we provide a slightly heavier mutation feature.