Gotchas

Every item on this page was found the same way: code that ran, produced numbers, and was wrong. None of them raised an error. Six of the seven are completely silent, and four produced plausible output — which is the dangerous kind.

Why this page exists

An array language compresses a lot of behaviour into very little text, so a one-character mistake is a whole different program rather than a syntax error. The defence is not vigilance, it is a differential test: run anything numerically important against a second implementation and compare exactly. Several of these were caught that way and no other.

1. A comment line of just / truncates the file

A line containing nothing but / starts a block comment. Everything after it in the file is silently ignored — definitions simply never appear, and referencing them later gives an unrelated "undefined variable" error somewhere else.

/ ---- section heading -----     OK: text follows the slash
/                                 <- this eats the rest of the file
f:{x+1}                           <- never defined

Keep at least one character after the slash on every comment line.

2. \l does not persist dotted names

Definitions with a dot in the name — bt.idx:{…} — do not survive a script load. They parse, they appear to work in the file, and they are gone at the prompt. Use camelCase (btIdx) for anything a loaded script must define.

3. Lambdas cap the number of locals

A lambda has a fixed maximum local count. Exceed it and the definition fails in a way that does not point at the cause. A seventeen-local function that refuses to load is not a syntax problem — split it into three smaller ones. This is inherited from the K core and is a real structural limit, not a tunable.

4. til #x parses as a train, not a count

til followed by # does not mean "the first n integers where n is the count of x". It parses as a composition and gives you something else entirely, without complaint. Write !#x.

5. Comments inside a multi-line lambda eat the rest of it

The loader joins the lines of a multi-line lambda before parsing, so a trailing / note on any line but the last comments out everything that follows it — including the closing brace and whatever came after. Keep multi-line lambda bodies comment-free and put the explanation above the definition.

6. A fully-applied projection under each evaluates once

This is the worst one on the page, because the output looks completely normal.

{[U] 3#U?U}[U]'til NB     / WRONG: returns the SAME basket NB times
{[i;U] 3#U?U}[;U]'til NB  / RIGHT: leave the iterated slot open

In the first form every argument is already supplied, so the expression is evaluated once and the result is repeated. You get a list of the right length, of the right type, containing the right kind of value — all identical. In a randomised sweep that produced a 13× flattering benchmark before anyone noticed the variance was zero.

The ten-second check

After any sampled or randomised each, run #?r — the count of distinct results. If it is 1 and you expected n, you have this bug.

7. An out-of-range gather returns nulls, silently

Indexing a vector past its end yields nulls where NumPy raises IndexError. The nulls then propagate through every downstream scan and reduction, and the run completes with output that looks like output.

This surfaced when a 3,600-day start grid plus 756-day observation offsets overran a 2,600-day price history: the entire tensor filled with nulls and every summary statistic came back plausible. Assert your bounds explicitly before the gather:

if[T <= (|/S) + |/off; '"price history too short"];

Habits that catch all seven

  • Differential-test anything numerical. A second implementation in a language with different failure modes — NumPy is ideal, because it raises where Amber returns nulls — catches items 6 and 7 immediately and items 1–5 by their consequences.
  • Check variance, not just shape. #?r on any randomised result.
  • Check for nulls after every gather that uses computed indices: +/^r should be 0.
  • Load your scripts in a fresh process and list what got defined. Items 1, 2 and 5 all manifest as "the definition is just not there".