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 scoping

Module scope

The module scope holds the file’s dims, shape aliases, domains, fns, extern fns, models, data and logs records, the other .ss files that import binds (Importing other files), and the Python modules that import python binds. import python torch.nn binds torch, as in Python, and importing the same module twice is fine. Declarations can be used anywhere in the file, before or after they appear, and dims may refer to each other in any order.

A module-scope name can be declared once (E0302), except functions that each take a different model first (Overloaded functions). A user domain can’t have the name of a built-in domain or a dtype (E0503).

Below the module scope is the prelude, the extern fn stubs every file sees (The prelude), and below that the built-in operations (Built-in operations). A declaration in the file hides a prelude stub or built-in of the same name.

Function scope

A function body starts with its parameters, as values, and the axis names its parameter types introduce. A statement x = e binds x for the rest of the body; binding the same name again replaces it for the code after, and the new value may have a different type. Inside a for or if block the rules are stricter, and what the block binds is its own (Blocks and scope). A function’s names are not visible in other functions.

A name used as a value is looked up in this order: the function’s values and axis names, the module scope, causal, the built-ins, then (in a notebook) Python variables. An unknown name is E0301, with a suggestion when a name with a similar spelling exists.

Using a name as the wrong kind of thing is E0303: calling a value or a dim, using a function or domain as a value, using a value as a size, or using anything but a domain as a domain. A dim or an axis name used as a value is its size, a Python int.

Axis names

An axis name is a name for a size. Axis names are introduced in four places:

  1. Parameter types. A name in a parameter’s type that isn’t in scope is a new generic axis, as b and n in fn f(x: [b n D]). The caller chooses its size (Generic calls). Later parameters can use it. A name in an Index[V] domain of a parameter introduces V the same way.
  2. Patterns. A name in a pattern that isn’t in scope is introduced with the size of the axis it binds to (Pattern operations).
  3. stack(..., over=k), when k is a new name.
  4. Keyword sizes in patterns, as h=8 in rearrange(x, b n (h d) -> b h n d, h=8).

Once introduced, an axis name is in scope for the rest of the function, or of the for or if block that introduced it: in later types, patterns, over= arguments, causal[n m], and expressions such as sqrt(Dh).

Return types and the types in as and to can only use sizes already in scope; any other name is E0301 (“unknown size”). For a size the function itself chooses, a return type writes ?k (Unknown sizes).

A dim is not an axis name, but it can name an axis: in [b n D] the third axis is named D and its size is the dim’s value. A pattern or type that uses a dim’s name requires that size.

Name clashes

Inside a function, a value can’t take the name of an axis in scope or of a dim, and a parameter can’t take the name of an axis an earlier parameter introduced (E0303). A value may take the name of a function, which it then hides for the rest of the body. Outside any fn in a notebook cell, a value can’t take the name of any declaration, since it would replace the declaration for every cell that uses it.