Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

6. The Milestone 1 keyword set

  • Status: Accepted — amended for Milestone 2 (see below)
  • Date: 2026-09-02

Amendment (2026-09-03, Milestone 2): four keywords were added for exceptions — try, except, finally, raise (ADR 0017) — taking the set from 19 to 23. Each is load-bearing and none has an alias: try / except / finally are three distinct block roles that cannot share a word, and raise is a statement, not a function (it unwinds; it has no return value and cannot be passed around). raise / except were already listed as "deferred to a later milestone with its own ADR" below. The module keywords import and as follow in the same milestone (that ADR carries their justification), which will bring the set to 25.

Context

"A small, deliberate set of keywords — nothing bloated" is a core design goal. The keyword set needs to be fixed early because it affects the lexer, the grammar, and what identifiers user code may use.

Decision

Milestone 1 defines exactly these 19 reserved words:

GroupKeywords
Functionsfn return
Conditionalsif elif else
Loopswhile for in break continue
Boolean logicand or not
Literalstrue false nil
Classesclass super
Empty blockpass

Rationale for the debatable members:

  • fn over def/func/function — shortest unambiguous choice.
  • elif over else if — one token, no nesting ambiguity, matches the single-keyword-per-concept style.
  • and/or/not as words, not &&/||/! — they read as English, they cannot be confused with future bitwise operators, and Korrin has no C heritage to honour.
  • self is not a keyword. It is a conventional identifier — the name of a method's first parameter (0005). Using it outside a method is an ordinary "cannot find self" error, which is clear enough; making it a keyword would buy a marginally better message at the cost of one more reserved word and a special case in the resolver.
  • super is a keyword, because super.method is special syntax: there is no value to bind a plain identifier to.
  • pass exists because indentation-defined blocks cannot be empty (0001).

Deliberately not keywords in M1: self, import, try, raise, catch, let, var, const, match, lambda, global, nonlocal, async, await, yield, with, del. Each is either deferred to a later milestone with its own ADR or ruled out by another decision (let/var by 0011; global/nonlocal likewise).

Consequences

  • The lexer's keyword table is a fixed 19-entry match.
  • import/try/raise land later and will each get an ADR when their design is settled; user code today may use those words as identifiers, and reserving them later is a documented breaking change. Accepted for a pre-1.0 language.
  • Booleans-as-words means not participates in operator precedence as a unary operator, handled in the Pratt parser.

Alternatives considered

  • Symbol operators for logic (&&, ||, !). No benefit here; costs readability and a lexer that has to distinguish &/&&.
  • Reserving the post-M1 words now. Would break fewer future programs, but reserves words for features whose design (and therefore whose exact keywords) is not yet decided. Prefer to reserve a word when its ADR lands.
  • def for functions. Fine, but fn is shorter and equally clear.