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
ifwith anelsebind, with the same shape, axis names and dtype, is in scope after theif. - 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
ifand the next iteration of a loop work whichever way the block went. A host scalar keeps its kind too: adding0.5to anintcounter 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 becomesReal. So a counter that starts at 0 and only goes up is in $[0, \infty)$, andy = y * 2in a loop leavesUnitvalues in $[0, \infty)$, which no longer fit aUnitreturn type (E0501).