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

Control flow

Loops

for i in a..b: runs its block once for each int from a up to, but not including, b, as Python’s range(a, b) does. The ends must be ints or Python values (E0803): a range counts on the host, so its ends can’t be tensors, even [] i64 ones. The loop variable is an int whose range goes from a’s lowest value to b’s highest value minus one.

for item in e: loops over a Python value e, and each item is a Python value (Python interop), or over a dataset’s batches, as in for batch in train |> batches(64):, and each item is a batch (Looping over batches). Looping over anything else, such as a tensor or a dataset that isn’t in batches, is E0804.

A return in a loop returns from the function. There is no while, break or continue.

Conditions

if c: runs its block when c is true, and the else: block, if there is one, when it isn’t. c must be a bool, a [] Mask or a Python value (E0802). Reading a [] Mask, as in if sum(x) > 0: or if any(x > 0):, waits for the device to compute it. A mask with axes isn’t true or false; reduce it with any or all. A Python value is true or false as Python says.

Blocks and scope

The block of a for, and each block of an if, are scopes of their own:

  • The loop variable, and the values and axis names first bound in a block, are out of scope after it (E0301).
  • A new value that both blocks of an if with an else bind, with the same shape, axis names and dtype, is in scope after the if.
  • A block may assign a value from outside it, which then has the new value after the block. The value must keep its shape, axis names and dtype, or stay the same model or a Python value (E0801), so that the code after an if and the next iteration of a loop work whichever way the block went. A host scalar keeps its kind too: adding 0.5 to an int counter in a loop is E0801. A loop whose variable has the name of a value from outside the loop assigns that value. To compute a value of another type, give it a new name.
  • A value’s domain may change in a block. After an if, it is the narrowest domain that holds what either block leaves, such as the hull of two ranges. A loop’s body is checked repeatedly, without reporting, until the domains at its start hold those at its end. A range that still grows after two rounds has its growing ends moved to infinity, and a domain that still changes after six rounds becomes Real. So a counter that starts at 0 and only goes up is in $[0, \infty)$, and y = y * 2 in a loop leaves Unit values in $[0, \infty)$, which no longer fit a Unit return type (E0501).