Preface
This is the second volume of Getting Started with EK9. The first taught the language by building small programs. This one is for people who already think in code: readers who finished the first volume and want the mechanisms behind what they used, and developers fluent in Java, C#, Kotlin, Rust, Go or Python who skipped it and want to know whether EK9 is worth their time. If you are in the second group, Where Volume 1 Left You, which follows, is a short compiled primer; read it first.
The book does three things, in order. It explains why the safety you used holds: the compiler reads your control flow, and each rule is a decision about what to forbid. It shows how to build real systems with the constructs the first volume only named: dependency injection, concurrency, a web service, packaging. And it ends on what the rules add up to: the quality the compiler enforces, and the two famous problems the design answers. Every chapter owns its costs. A rule that forbids something takes something away, and the chapter says what, and what it buys.
The rest of this preface is an essay. It is the argument for the whole design, and the one place the book steps back.
The decade that still runs your code
Open almost any popular language and you are looking at decisions made between roughly 1969 and 1995. C arrived in 1972, C++ in 1985, Java and JavaScript in 1995. Python (1991) and the rest borrowed liberally from the same well. Those designs were shaped by constraints that have since evaporated: a few kilobytes of memory, single processors, no practical type theory in compilers, and a hard requirement that the compiler itself stay small enough to run on the hardware of the day.
Under those constraints, the choices were sensible. A null pointer was a cheap way to say "nothing here." Letting a
programmer break out of a loop or goto a cleanup label saved instructions. Leaving quality to convention kept the
compiler simple. None of this was foolish. It was appropriate for 1975.
The trouble is that those choices hardened into instincts, and the instincts outlived the constraints. We now have gigabytes of memory, many cores, mature type theory, and compilers that can afford to think hard. Yet we still write code as though a value might secretly be nothing, as though jumping out of the middle of a block were normal, as though catching quality problems were someone else's optional tooling. We inherited the workarounds and forgot they were workarounds.
EK9 is a bet that we can do better by taking the last fifty years of evidence seriously. Not because its designer is cleverer than the people who built C - they were giants - but because EK9 is later. It has the post-mortems they could not have read.
What the post-mortems say
Nearly every constraint EK9 imposes has a recorded failure behind it, not just a taste. The appendix The Evidence gathers the sources; four are enough here.
- The null reference was introduced, by Tony Hoare's own account, in 1965. In 2009 he called it his "billion-dollar
mistake". Java programmers know its cost as
NullPointerException. - Jumping out of the middle of a block is the oldest complaint of all. Edsger Dijkstra's "Go To Statement Considered
Harmful" (1968) argued that unrestricted jumps make a program hard to reason about.
breakandcontinueare smallgotos. - The
goto failbug (Apple, 2014, CVE-2014-1266) skipped TLS signature verification on iOS and macOS because of one duplicated jump. It compiled cleanly. switchfall-through is a recorded weakness: CERT's coding standard covers it in recommendation MSC17-C, and the Common Weakness Enumeration lists it as CWE-484, "Omitted Break Statement in Switch".
A language designed in 1975 could not have weighed this evidence, because it did not exist yet. A language designed now has
no excuse to ignore it. The same holds for instanceof plus a cast: it quietly breaks Liskov substitution, the
property that makes polymorphism trustworthy.
The mapping
Here, then, is the bridge: each inherited assumption, what we have since learned, and the answer EK9 gives. The chapters that follow teach the right-hand column; this table is the map.
| 1970s assumption (C lineage) | What we learned | EK9's answer |
|---|---|---|
null / nil is an ordinary value |
Hoare's "billion-dollar mistake" | No null keyword; the tri-state (absent/unset/set) with ? |
Data is mutable by default; protect the data with const / final / val |
Immutability-as-data is rigid; aliasing still bites | Data is always mutable; the process (pure) governs when mutation may happen |
break / continue / return / goto to leave a block early |
Jumps make a program hard to reason about (Dijkstra, 1968; goto fail, 2014) |
Removed; stream pipelines and guards express the intent directly |
switch fallthrough |
Forget one keyword, run the wrong cases (CWE-484) | No fallthrough; one case lists several values |
Runtime type checks: instanceof + casting |
Breaks Liskov substitution; brittle | The dispatcher routes on argument types - no casts |
| Inheritance first; classes open by default | The fragile base-class problem | Closed by default; composition and trait delegation preferred |
| Quality is bolt-on: linters, SAST, coverage, style - all optional | Optional checks are skipped under deadline pressure | The compiler is the single gate: complexity, purity, coverage, security |
| Operators have loose or ad-hoc meaning | Surprising operator behaviour breaks substitution | Finite, strict operator contracts every type must honour |
| Internationalisation via external libraries and config files | Bolted on; drifts out of sync | Localisation (text) is in the grammar |
| Functions are second-class citizens beside objects (or objects beside functions) | A false dichotomy that distorts designs | Functions and classes are equal partners; functions are safely polymorphic |
| Dependency injection via reflection and frameworks | Wiring errors surface at runtime | DI is a language construct, validated at compile time |
Two ideas do most of the work
If you remember only two things from this essay, make them these, because every row above ultimately rests on them.
Tri-state, not null. In EK9 a value is absent, unset, or set, and the ? operator asks whether it is set. There is
no null keyword and no way to write one. A value that exists but has nothing meaningful in it is unset, an ordinary
state you can test and handle; a variable declared but not yet assigned is a null slot, and the compiler's job is to
prove it is never read. That is the claim, stated as carefully as the language's governing document allows: EK9 is
designed so that absence is unobservable; the compiler checks every read; the proof is in progress. Every read position
in the grammar is classified and checked today. The layers that would turn "no counterexample found" into a theorem are
still being built, and this book says designed so that, not guaranteed, until they are.1
Purity is a property of the process, not the data. This is the one that most surprises experienced developers,
because it inverts the const/final/val reflex. EK9 has no immutable data structures; every record, class and
collection is mutable. Safety does not come from freezing the data - it comes from the function, method or
operator deciding when mutation is permitted. A pure process may build values and set its own return, but it may not
reach out and change what already exists, and it may call only other pure processes. Mutation becomes something you
grant, not something you fight.
Put those together - values the compiler checks before every read, and operator and override rules a subtype cannot quietly betray - and Liskov substitution has far fewer ways to break. The compiler keeps those rules, rather than a team promising to; the last chapter looks at how far they go, and where they stop.
Why this is hard to teach, and worth it
A book about a language in the 1970s lineage can reuse the examples its predecessors used and simply change the syntax. A loop is a loop; a null check is a null check. This book cannot do that, because the whole point of EK9 is the places where it refuses to be the same. Its job is harder: to take a concept you already hold, show honestly where EK9 constrains it or extends it, prove the constraint with evidence rather than assertion, and then show the EK9 idea that does the job.
That is the bridge. On the near bank, fifty years of accumulated workarounds we mistook for the nature of programming. On the far bank, a language that asks a simple question of each one - does this eliminate a category of failure, or merely discourage it? - and keeps only the answers that eliminate.
A word about maturity
EK9 is pre-beta: a beta is planned for mid-2027, and no production system runs on it yet. Where this book shows a gap - a rule the compiler does not yet enforce, a feature that is planned rather than built - it is a real gap, and it is named where it bites, not softened. What you can check is narrower and firmer: every example in these pages was compiled and run with the EK9 compiler, and every block of output is what that run printed.
-
EK9's design document on absence lists every route by which an absent value could be read, closes each, and says which layers of the proof are built and which are not. Hoare's own account is in The Evidence, the appendix. ↩