The laboratory notebook page behind the Status notes added to Parts I–III. Nothing here is normative: the rule is in the specification, and this is the evidence that each note says the truth.
Every row below is a .nika file of one or two lines, written to use exactly one
construct and handed to the compiler as
nikaia lower <probe>.nika -o <probe>.rs
on the release build of the branch’s origin/main base. The Result column
records what happened, in three kinds:
Four rows were re-run after the run below, because the parser changed under
them: the three trailing-lambda rows are now built, and the row they were
re-run beside — fn(a) sync { … } — is unchanged. Those four say “re-run” in
the Result column; every other row is the original run.
| Construct | Specified in | Result |
|---|---|---|
trait Name { … } |
I 4.7 | parse error: expected end of input; found trait |
pub trait Name { … } |
I 4.7 | parse error: expected one of enum, struct; found trait |
impl Name for T { … } |
I 4.7 | built |
[1, 2, 3] |
I 4.5, II 12.6, III 14.4 | parse error: expected expression; found [ |
[] |
II 12.6 | parse error: expected expression; found [ |
-> [User] (list type) |
III 14.3 | parse error: expected type; found [ |
Vec[i64], xs[0], xs[k] = v |
I 4.5 | built |
macro Name(…) -> AstExpr { … } |
II 10.3 | re-run, and withdrawn: macro is a reserved word that is not a construct (ADR-304), so the parse error now carries “macro is a reserved word, so it is not a name”. 10.3 no longer specifies a macro system |
quote { … } |
II 10.3, 10.4 | it used to parse as let q = quote; then { 1 + 1 }, then was refused by NK1117 — and is now refused by the grammar, which is earlier and better: quote is a reserved word that is not a construct (ADR-304). It never meant something else in silence again |
struct User with Describe { … } |
II 10.3 | re-run, and withdrawn: same as the two above — the parse error now names with as a reserved word. Nothing is attached to a declaration, because nothing is generated (ADR-304 D1) |
const NAME: T = … (item) |
II 10.2, I 9.2 | parse error: expected end of input; found const |
const NAME = … (in a body) |
II 10.2 | parses as const; LIMIT = 10; — re-run, and refused now: NK1117 on the name |
select { … } |
II 12.4 | parse error: expected expression; found > (at the arm’s =>) |
test "name" { … } |
III 14.1 | built (ADR-269 D1): compiled only by nikaia test, each run in a process of its own |
test "name" (u: User) { … } |
III 14.3 | parse error: expected end of input; found test |
bench "name" { … } |
III 14.4 | parse error: expected end of input; found bench |
assert cond |
III 14.1, 14.2 | parses as assert; b != 0; — re-run, and refused now: NK1117, “nothing declares assert, and this statement is just that name”. The assertion still does not exist; it no longer disappears in silence |
assert cond, "message" |
III 14.2 | parse error: expected }; found , |
assert(cond), assert(cond; message: …) |
III 14.2 | built (ADR-269 D2, D3): a prelude function the compiler knows; a condition that could pause, fail, touch or change anything is NK1194, another shape NK1195, and a false one stops the program naming its line, its claim and its operands’ values |
extern "C" { … } |
III 15.1 | parse error: expected end of input; found extern |
unsafe { … } |
III 15.1, 16.3 | parses as unsafe; { puts("hi") } — re-run, and refused now: NK1117 on the name |
break, continue |
I 3.3 | built (ADR-287): name for name, and the rule that bounds them is NK1126 — a jump may not leave a lambda, a task, an overlap branch or a DSL fold’s step, each being a function in the language below. A row added by a construct arriving rather than by a probe: before the record the two words parsed as nothing at all, since ADR-276 had reserved them, so the probe that belongs beside the others here is break x — refused, NK1127, because a jump takes no value and a value after one would otherwise be a statement of its own |
null |
I 2.3, 3.5 | built (ADR-278): a reserved word, lowering to None |
String?, ref String? |
I 2.3 | built: Option<String>, Option<&str> — and the wrap where a plain value stands in a nullable slot is the compiler’s to write, in all five positions. Some(…) where the type is known, .into() where it is not, which is right either way (ADR-278) |
a?.b |
I 3.5 | built (ADR-278 D9): map over a plain field, and_then over one that is itself a T? |
a?.m() |
I 3.5 | built (ADR-278 D10): the section’s word is member and a method is one. A match and not the field’s map, because a method may pause and may fail |
a ?? b ?? c |
I 3.5 | built (ADR-278 D15): right-associative. It used to be a parse error naming the second ?? |
a?.b.c |
I 2.3, 3.5 | refused, NK1125 (ADR-278 D14): a ?. guards its own member and no more, and a T? is a type of its own — so the unguarded .c is a member the type does not have. a?.b?.c is the program |
a ?? 1 |
I 3.5 | built: a.unwrap_or_else(\|\| 1.into()) |
f(a??) (postfix unwrap) |
— ADR-289 D19 and III 17.1 write one; I 3.5 does not define it | parse error, and since ADR-279 D2 it is that record’s sentence: expected one value, or an expression in brackets — the ?? is read as the infix one missing its fallback, which is what it is. The two examples that write one are on [#191](https://github.com/Nikaia-Language/Nikaia/issues/191)’s list, as two examples write a postfix ?? the language does not have |
Conn(id: 1) (same file) |
I 4.2 | built |
http::Response(status: 400, …) |
III 17.1, ADR-289 D19 | parses, and lowers to http::Response { status: 400 } — the qualified path used to be the parse error here (expected one of ), ,; found :) and is not any more. What is missing is http itself, which is a library and not a syntax ([#90](https://github.com/Nikaia-Language/Nikaia/issues/90), there is no HTTP server) |
use a::{b, c} |
II 10.5 | re-run, and a sentence now: “names are not brought in; a package is reached through its name. Write use http, and http::Request where you need it — and use http as h if the prefix is long”, with the caret on the brace (ADR-286 D11) |
use std::fs, use utils |
I 9.1 | built |
Point { line, .. } (pattern) |
I 7.1 | parse error: expected }; found . |
Point { line } (pattern) |
I 3.4 | built |
_ => throw error (arm body) |
I 7.1 | parse error: expected one of (, { |
_ => { throw error } |
— | built |
fn f(g: fn()) (function type) |
I 5.4 C, III App. B.2 | parse error: expected ); found ( |
fn f(g: @detached fn()) |
I 5.4 C | parse error: expected type; found @ |
x.m fn { … } |
I 5.3 | built |
x.m(a) fn { … } |
I 5.3 | built |
x.m fn(a) { … } |
I 5.3, II 12.3 | built (re-run after the trailing lambda took a parameter head): users.map(\|user\| { … }) |
f(a, b) fn(x, y) { … } |
II 12.3 | built (re-run): access_all(a, b, \|x, y\| { … }) |
p::q fn(s) { … } / p::q fn { … } |
II 12.7 | built (re-run): task::scope(\|s\| { … }) — the callee has no std entry, so the call is what is built |
fn(a) sync { … } |
I 7.2 | parses, means something else: fn(a); sync { a }; — re-run, and refused since sync became a reserved word (ADR-298): expected {; found unexpected token sync, at the annotation. What is unbuilt is unchanged — a parameter list is still followed by the body and by nothing else — and the three statements are gone |
1_000 |
I 2.2 (silent) | parses, means something else: let x = 1; _000; |
0xFF |
I 2.2 (silent) | parses, means something else: let x = 0; xFF; |
1i64 |
I 2.2 (silent) | parses, means something else: let x = 1; i64; |
/* … */ |
I ch. 2 (silent) | parse error: expected }; found / |
/// doc |
I ch. 2 (silent) | parses as an ordinary // comment |
unterminated "… |
I 2.5 (silent) | the newline is inside the literal; the literal runs to the next " or to end of file |
nikaia --help on the same build offers build, run, lower-std and help.
| Command | Specified in | Result |
|---|---|---|
nikaia build, nikaia run |
III 13.2 | built |
nikaia lower-std |
III 13.2b | built |
nikaia new |
III 13.1 | error: unrecognized subcommand 'new' |
nikaia test |
III 13.2 | built (ADR-269 D1, D12), with --both-settings |
nikaia bench, nikaia fmt |
III 13.2 | not present |
nikaia explain <code>, nikaia explain --tethers |
I 6.6, 7.1 | not present: nikaia explain takes a .nika file (ADR-260 D3), so NK-2C7 is read as a file name |
nikaia build --with-asserts |
III 14.2 | withdrawn (ADR-269 D4): no build removes an assert, so there is nothing for the flag to put back |
nikaia bench --history |
III 14.4 | no such command |
nikaia lower, nikaia interpret, nikaia explain <file> |
III 13.2 | built (ADR-260 D3); --input and --backend are gone (D5) |
--locked, --overlaps, --trust, --no-cache, --target, --user-parallelism |
III 13.2, 13.5, I 8.1.2 | built |
Note that nikaia explain <file>.nika (read rustc JSON on stdin) and Part I’s
nikaia explain <code> (from an error’s site to the line that raised it) are
two different things; only the first exists.
Codes the compiler emits: NK1101–NK1113, NK2202, NK2501, NK2502,
NK2605, NK2701. Codes Part III C.3 catalogues and nothing raises: NK2101,
NK2102, NK2201, NK2301, NK2401, NK2601, NK2602, NK2603, NK2604.
The two lists above are this page’s own reading on the day it was written, and they have moved since; Part III C.3’s status note is the current one.
NK2101has crossed over —spawnlowers, so there is a task to hand data to (ADR-055 §6 step 4) — and so have severalNK11xx. The lists are left as they were, for the reason the whole page is: a page that quietly updates its findings cannot be read against the day it was written.
NK2605 was added after this page was written, for the shape
from-for-throws-and-touches.md §6 found: a
written call that can fail, in a function that declares nothing.
Three things were not decided here and the specification was unchanged at every one of their sites. The first two have since been decided; the notes are kept with what they were, because a page that quietly loses a finding cannot be read against the day it was written.
spawn. The specification writes spawn fn { … } twelve times
— six in Part I (5.4 B, 8.2, and four in 8.3, two of them inside NK2101’s
own text and help:) and six in Part II (11.2, 12.5, two in 12.7, and two
inside NK2102’s text and help:) — and the parser is spawn "(" expr ")".
spawn fn { … } parsed as spawn; || { … };, two statements.
Re-run after the trailing lambda reached a path. It is now one
expression, spawn(|| { … }), and spawn fn(x) { … } likewise — a call to a
function named spawn, which is what the source says and what nothing
provides. rustc moved from cannot find value 'spawn' in this scope to
cannot find function 'spawn' in this scope: the same E0425, one noun
closer to the truth, and still an error. Whether spawn is a keyword form
that becomes a task or a std function that takes a lambda is the decision
left here; nothing in the parser’s spawn rule changed.
Re-run again, after the reserved-word list. spawn is a reserved word
now (ADR-298), so there is no variable and no
function of that name for the line to be read as: spawn fn { … } is
expected (; found unexpected token fn, this compiler’s refusal at the
fn rather than rustc’s about the generated file. Both readings above are
gone and the decision left here is untouched — what spawn means is
still #219’s spawn entry, and the parser’s spawn "(" expr ")" rule is still
what it was.
The spelling is settled; the rest of this entry is not. spawn fn { … }
is the one form, in all three Parts — the parenthesised spawn({ … }) that
Part III Appendix C.5’s NK2501 example wrote is the thirteenth site this
page did not count, and it is now spawn fn { … } like the other twelve.
That closes the disagreement between two normative documents and nothing
else: spawn is a call whose last argument is a lambda, which is the trailing
lambda Part I 5.3 already has, and the parser’s spawn "(" expr ")" rule is
still what it was. So the compiler-against-specification half of this entry
stands exactly as written above.
Closed (ADR-055 §6 step 4). The decision
left here — “whether spawn is a keyword form that becomes a task or a
std function that takes a lambda” — turned out to be answered by the thing
nobody had asked: what a task means at user_parallelism = no. Part II
11.2 says “interleaved on the same thread”, two synchronous Rust closures
cannot interleave, and the emitted Rust contained the word async zero
times. So the lowering moved, and with an executor to run it spawn is a
keyword form: TaskHandle::start(async move { … }), with the body an async
block because a task’s body is a block rather than something called, and
a closure around it would only be called once. (Not because Rust has no
stable async closure — it has one, and this line used to say otherwise:
ADR-277 D13, D16.) What settles it against a
std function taking a lambda is Part II 11.2 above and the parser: the
rule is now the trailing lambda, so spawn fn
{ … } parses as the one form, and the parenthesised spelling says what
happened to it. Twelve sites, one reading, one rule.
throws with a type.throws names no types and the parser agrees; the four sites that
contradicted it — Part I 6.4 once in code, twice in prose and once inside
NK2601’s own help:, and Part II 10.2 B’s throws ParseError — are
corrected to Part I 7.1’s form. throws IoError still does not parse, and
the refusal is now a sentence rather than a parse error at the type name:
kw_throws carries a fail(…) citing ADR-023
D1 and pointing at nikaia.contracts, on the precedent of ADR-277’s removed
fn: … form in the same file. The help: line was the worst of the four,
because the compiler’s own suggested fix told the user to write what the
compiler refuses.macro and quote with nothing underneath themcomptime plus a bound is enough.
10.3 is rewritten around that and 10.4’s hygiene question dissolved with the
construct it applied to. The three words are reserved and are not
constructs, which is the one thing the pages now promise.
Part III Chapter 14 is still a whole chapter; it carries a Status note, and
whether a chapter specified that far ahead should stay is a decision, not a
documentation defect.