Finding
docs/SPEC.md:626 states:
One rule, one sentence: brackets after of are an argument list; parentheses are one argument.
docs/LANGUAGE_CONTRACT.md:208-213 repeats it:
f of [a, b] — two arguments. So momentum of [2, 3] passes…
- Extra elements are ignored; parameters with no matching element take their default, else
null.
Neither holds for a 1-parameter callee.
Reproduction
define one(a) as:
return a
print of ["one of [3, 4] ->", one of [3, 4]]
print of ["type ->", type of (one of [3, 4])]
["one of [3, 4] ->", [3, 4]] <- docs promise a = 3
["type ->", "list"]
one of [3, 4, 5] → [3, 4, 5]. Identical under EIGS_JIT_OFF=1 and for lambdas. --lint reports "no issues found".
A reader of either doc writes one of [3, 4] expecting a = 3, gets the whole list, rc=0.
The docs are the wrong side — the behavior is load-bearing
src/vm.c:1795-1806 / 1822-1837 re-collect all args into a list when param_count == 1 && argc != 1 && !can_default. That is exactly what makes these work:
len of [1, 2] -> 2 (not len of 1)
print of [1, 2] -> [1, 2]
Removing it would break the language. So the real rule is arity-dependent: for a 1-param non-defaulted callee, f of [] → [], f of [x] → x, f of [x, y] → [x, y].
This is the skill's counter-rule in action — the "silent" path is the semantics, not a bug. Checked before condemning it.
Why it matters
of is the language's single most distinctive syntax, and the doc's whole selling point is that it's one sentence. It isn't. The carve-out is invisible until a user writes a 1-param function and passes it a list — at which point they get a silently wrong value, not an arity error.
Note the f of [] half of the carve-out is already documented at LANGUAGE_CONTRACT.md:264-268. The argc >= 2 half is documented nowhere.
Fix (docs only)
docs/SPEC.md call section + the evaluation-model reference: state the arity-1 carve-out next to the "one rule" sentence.
docs/LANGUAGE_CONTRACT.md argument-unpacking section: same, alongside the existing f of [] note.
- Add an executable-doc example so the gate pins it:
define one(a) as:
return a
print of (type of (one of [3, 4]))
→ list
- Consider extending lint W017 (which already flags the 1-element bare form) to flag a bare multi-element list passed to a statically-known 1-param callee — that's the case where the user's intent and the semantics most likely diverge.
Finding
docs/SPEC.md:626states:docs/LANGUAGE_CONTRACT.md:208-213repeats it:Neither holds for a 1-parameter callee.
Reproduction
one of [3, 4, 5]→[3, 4, 5]. Identical underEIGS_JIT_OFF=1and for lambdas.--lintreports "no issues found".A reader of either doc writes
one of [3, 4]expectinga = 3, gets the whole list, rc=0.The docs are the wrong side — the behavior is load-bearing
src/vm.c:1795-1806/1822-1837re-collect all args into a list whenparam_count == 1 && argc != 1 && !can_default. That is exactly what makes these work:Removing it would break the language. So the real rule is arity-dependent: for a 1-param non-defaulted callee,
f of []→[],f of [x]→x,f of [x, y]→[x, y].This is the skill's counter-rule in action — the "silent" path is the semantics, not a bug. Checked before condemning it.
Why it matters
ofis the language's single most distinctive syntax, and the doc's whole selling point is that it's one sentence. It isn't. The carve-out is invisible until a user writes a 1-param function and passes it a list — at which point they get a silently wrong value, not an arity error.Note the
f of []half of the carve-out is already documented atLANGUAGE_CONTRACT.md:264-268. Theargc >= 2half is documented nowhere.Fix (docs only)
docs/SPEC.mdcall section + the evaluation-model reference: state the arity-1 carve-out next to the "one rule" sentence.docs/LANGUAGE_CONTRACT.mdargument-unpacking section: same, alongside the existingf of []note.list