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

Names and scopes

Binding names

= binds a name to a value and returns Nothing. : does the same but returns the value.

x = 10               # => ()
x                    # => 10
y : 20               # => 20
y                    # => 20

= is handy in a sequence of steps, where the Nothing disappears (see Sequencing). : is handy when you want to keep the value, for example to show it in a template and remember it at the same time.

Binding a name again replaces its value in the current scope. The left side of = and : must be a name, a tuple of names, or (). Anything else gives Undefined and binds nothing.

1 = 2                # => Undefined

Assignments do not chain: a = b = 1 and a : b : 1 give Undefined. Write a = 1, b = 1 instead.

Destructuring

A tuple of names binds each name to the matching item:

(a, b) = (1, 2)
a                    # => 1
b                    # => 2
(a, b) = (b, a)
a                    # => 2

When there are more names than items, the extra names are bound to Nothing. When there are more items than names, the last name receives all the remaining items:

(first, rest) = (1, 2, 3)
first                # => 1
rest                 # => (2, 3)
(p, q, r) = (1, 2)
r                    # => ()

The same rules apply to function parameters.

Unbound names

A name that is not bound anywhere evaluates to Nothing. This is not an error, so a misspelled name silently gives Nothing. The alternative operator turns that into a default value:

title ; "Untitled"   # => "Untitled"
title = "Report"
title ; "Untitled"   # => "Report"

Scopes

A scope is a table of names. Each scope (except the outermost) has a parent, and looking up a name searches the scope itself, then its parent, and so on.

New scopes are created by:

  • function calls: the body of a function runs in a new scope, whose parent is the scope where the function was defined;
  • namespaces: the expression inside { } runs in a new scope, whose parent is the current scope.

ns.expr evaluates expr in the scope of the namespace ns.

Binding a name always affects the current scope. Inside a function, = creates a local name, even when an outer scope has the same name:

count = 0
increment = () -> (count = count + 1, count)
increment()          # => 1
count                # => 0

The outermost scopes hold the built-in names. Your code runs in a scope below them, so you can reuse a built-in name without changing the built-in.

Global names: bind and label

bind and label bind names in the global scope, the scope where top-level code runs, wherever they are called from. They take the name as text, which makes them useful inside functions and in promise callbacks:

remember = v -> bind "last" v
remember 42
last                 # => 42
label "answer" 42    # => 42
answer               # => 42

bind name value returns Nothing, like =. label name value returns the value, like :. Both accept a tuple of names. See Root names.

Lifting: $name

$name reads name from the outermost scope that binds it, skipping the nearer bindings that hide it:

x = 1
f = () -> (x = 2, (x, $x))
f()                  # => (2, 1)

The operand of $ must be a name or a tuple of names.