# Truth, iteration and values

## Truth and staging

`nil` and `false` are false; every other ordinary value is true, including
zero, the empty string and an empty vector. `if`, `when`, `unless`, `cond`,
`and`, `or`, `not`, `truthy`, `filter` and `assert` all use this rule.
`and` and `or` return the deciding value itself, not a boolean.

A tensor, a parameter or a `ctx` scalar used as a condition is
`E-FLOW-001`. Shapes are static and both branches must exist in the graph,
so select elementwise instead:

```lisp
(if (> x 0.0) x 0.0)              ; E-FLOW-001: x is a tensor
(where (> x 0.0) x (zeros-like x)) ; the graph computes both, picks per element
(if (> (dim x 0) (dim x 1)) ...)  ; fine: dim is an ordinary integer
```

Shape queries (`shape`, `rank`, `dim`, `numel`, `dtype`) return ordinary
values, so compile-time decisions on shapes are plain `if`s. That is how
Muon transposes a tall matrix; see [sexpgpu/optim](https://sexpgpu.041.io/docs/optim.md).

## Iteration

| form | behavior |
|---|---|
| `(dotimes (i count [result]) body...)` | evaluates `count` once, runs the body for `i` from 0 below it; a negative count runs nothing; `result` sees `i` equal to the iteration count |
| `(dolist (item list [result]) body...)` | evaluates the list once, runs the body per element; `result` sees `item` bound to `nil` |
| `(repeat n f)` | calls `f` with `0..n-1` and collects the results: how a stack of blocks is built |
| `(map f xs)`, `(filter f xs)`, `(reduce f init xs)` | collecting and folding; see [builtins](https://sexpgpu.041.io/docs/builtins.md) |

`dotimes` and `dolist` collect nothing: they return `result`, or `nil`
without one, and preserve its multiple values. Their loop variable is one
binding shared across iterations. With a tensor accumulator they unroll
into the graph:

```lisp
(let ((result (zeros-like x)))
  (dotimes (i degree result)
    (setq result (+ result (* (sum (slice coefficients 0 i (+ i 1))) (pow x i))))))
```

## Multiple values

`(values expr...)` returns zero or more values. Ordinary arguments,
variable initializers, assignments, vector elements and training contract
components take the primary value; zero values give `nil` there. The last
form of a body and the chosen branch of `if` keep all values.

| form | behavior |
|---|---|
| `(values-list list)` | the list's elements as values |
| `(multiple-value-list expr)` | all returned values as a list |
| `(multiple-value-bind (names...) expr body...)` | binds the values; missing ones are `nil`, extras discarded |
| `(multiple-value-call f expr...)` | calls `f` with every value of every `expr`, concatenated |
| `(multiple-value-prog1 expr body...)` | returns `expr`'s values after running the body |

`sexpgpu eval` prints several values as `#<values ...>`.

## Errors on purpose

- `(error message...)` stops compilation with `E-EVAL-001` and the message.
- `(assert test [message])` stops with `E-EVAL-002` when `test` is false:
  `(assert (= 2 (rank param)) "muon updates matrices")`.

Both point at the line that raised them, or at your model application when
raised inside a library. See [check](https://sexpgpu.041.io/docs/check.md#where-a-diagnostic-points).

Related: [It is Common Lisp](https://sexpgpu.041.io/docs/lisp.md), [tensor operations](https://sexpgpu.041.io/docs/tensors.md).

---

SexpGPU documentation. Every page: https://sexpgpu.041.io/llms.txt
