Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Let Binds

Let bindings introduce names that are visible in their scope after they are defined.

// compile error x isn't defined yet
let x = 2 + 2 + x;
// ok
let y = x + 1

The same name can be used again in the same scope, it will shadow the previous value.

let x = 1;
// ok uses the previous definition
let x = x + 1;
// true
x == 2

You can annotate the binding with a type, which will then be enforced at compile time. Sometimes this is necessary in order to help type inference.

// x may be null later, so its type is the union, though it starts as an i64
let x: [i64, null] = 1;
// compile time type error: string does not contain [i64, null]
let y: string = x;

You can use patterns in let binds as long as they will always match.

// binds x to 3 and y to "hello"
let (x, y) = (3, "hello");
// true
x == 3;
// true
y == "hello"

You can mix type annotations with pattern matches

let (x, y): (i64, string) = (3, "hello")

To document the public API of a module, use /// documentation comments in interface files. Documentation is displayed in the shell during tab completion and made available by the LSP server.

Comments

A // comment runs to the end of its line and belongs to whatever comes next: write it on its own line directly above an expression, a select arm, a method inside an impl block, or a field of a struct literal.

// a counter, one step per cycle
let n = 0;
n <- n + 1;
select n {
  // nothing has happened yet
  0 => "waiting",
  // every later step
  k => "step [k]"
}

Anywhere else is a parse error — after an expression on the same line (x + 1; // no), between an operator and its operand, or dangling before a closing } with nothing below it. The rule is what lets the parser keep every comment in the syntax tree, so tools that rewrite a program (the formatter, the REPL echo) never lose one.