6. The keyword set
- Status: Accepted — amended for Milestone 2 (see below)
- Date: 2026-09-02
Amendment (2026-09-03, Milestone 2): six keywords were added, taking the set from 19 to 25.
- Exceptions (ADR 0017):
try,except,finally,raise. Three distinct block roles that cannot share a word, plusraise, which is a statement (it unwinds — no return value, cannot be passed around).raise/exceptwere already listed as "deferred to a later milestone" below.- Modules (ADR 0018):
import,as.importis a statement (it runs a file and binds a name — not an expression);asis the rename clause, shared with no other construct.importwas likewise already listed as deferred.None of the six has an alias. The set is larger than the "small, deliberate" goal implied at 19, but each word buys a whole capability (structured error handling; multi-file programs) that the language could not express at all before, and the total is still well below Python's ~35.
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:
| Group | Keywords |
|---|---|
| Functions | fn return |
| Conditionals | if elif else |
| Loops | while for in break continue |
| Boolean logic | and or not |
| Literals | true false nil |
| Classes | class super |
| Empty block | pass |
Rationale for the debatable members:
fnoverdef/func/function— shortest unambiguous choice.elifoverelse if— one token, no nesting ambiguity, matches the single-keyword-per-concept style.and/or/notas words, not&&/||/!— they read as English, they cannot be confused with future bitwise operators, and Korrin has no C heritage to honour.selfis 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 findself" 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.superis a keyword, becausesuper.methodis special syntax: there is no value to bind a plain identifier to.passexists 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/raiseland 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
notparticipates 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.
deffor functions. Fine, butfnis shorter and equally clear.