Skip to content

Rationale · essay 01

Philosophy

rationale.md · 89 lines · 4 min read

The root mental models from which everything derives. They were fixed before the syntax, because coherent syntax is not designed keyword by keyword.

Most languages grow by accretion: match is decided today, pointers next week, declaration later. They are three local decisions that do not talk to each other, and the result is a patchwork where each new concept never quite fits. Makoto inverted the order: define the cross-cutting principles first, and let each construct fall almost by itself from them.

There is a hierarchy of principles, from the most applied to the most general:

  1. Opt-in test (the metric): you only pay for what you summon. A concept is expensive if those who do not use it need to understand it. From there follows the corollary: the default is always the unmarked one. Private is bare, pub is the mark; immutable is the bare :=, var is the mark; safe is the normal, unsafe is the mark. This is not a separate principle; it is opt-in applied to each axis.
  2. Non-poisoning (the local application): each subsystem carries only its own colors. The memory interfaces do not say which memory; IO does not say sync/async; the global namespace carries no function at all. A subsystem cannot “poison” the others by leaking its details.
  3. Systems-and-boundaries / tainted coloring (the general law): the one that governs the two above. Every color belongs to a realm; the sin is the color of one realm staining another. “Poisoning” is just the name of that stain seen from inside.

And two form rules that fall out of these principles:

  • Unification by concept. When N keywords exercise the same verb on different objects, they become one. switch/match/select match (the object disambiguates); for/while/infinite-loop loop. But, and this is the fine point, “same verb” means same operation and same realm, not same form. struct, enum and interface have bodies of different shape and still unify under decl, because they are the same act (declaring a type) in the same realm (the type system). union has a body identical to a struct’s and still does not unify, because it is another operation (reinterpreting bytes) in another realm (the unsafe one). Unifying by surface similarity would be committing sin #3 itself.
  • Axes are preserved, not fused. let/var/const look like “three forms of declaring”, but they are a mutability axis with a default. Fusing them into a bind(mode=mut) would erase the axis: the distinct names are the visual information, not noise. The rule: if it is one act with different objects, unify; if it is one act with an axis-of-modes, preserve the axis.

And the architecture that all of this projects onto the ecosystem: the tiers (ch. 11), with layered stability, the add-only core at the bottom and what changes faster living higher up.

The principles are not aesthetics; they are a decision machine. When a proposal arrives (“what if we gave a separate keyword for each way to import?”), it is tested against the principles instead of taste. The answer was to unify everything under use (importing is a single act): it is the form after the keyword that carries the intent. use json qualifies (json.parse), use json.{parse} lifts the name (parse), use json as j aliases. One keyword, three intents in the syntax, without three words. And when the principle predicts a choice you had already made by instinct (private-bare, UTC-in-core and IANA-opt-in), it is a sign that the principle is real, not imposed.

  • Why not design keyword by keyword? Because it generates inconsistency: local decisions that do not talk to each other. Principles first is what gives few keywords and coherence at the same time.
  • Why not unify everything that looks alike (struct/interface under a single type)? Because it would erase the boundary between data and dispatch, exactly what law #3 forbids. The form deceives; what counts is operation + realm.
  • Why not convention (capitalization = public, à la Go)? Because scope should not depend on attention-dependent convention; it should be an explicit mark. It is opt-in applied to visibility.

Without explicit principles, the raw count of decorators is scary; “so many loose decorators?” looks like chaos. Reorganized, they are the index of 4 to 5 subsystems (@mm is memory; @supervisor/@transfer are concurrency; @cimport/@asm are FFI). The raw count is noise; grouped by system, it is organization. The principle is what turns “an arbitrary list” into “a map”.

The default is the unmarked one; you mark when you leave the common case. And one unifies by operation + realm, never by form.

The discipline costs consolidation. You cannot simplify struct/interface into a single keyword, even when it looks tempting, because it violates the boundary. The @ sigil ends up indexing 6 to 7 subsystems (visually denser than a handful of keywords would be), and the axes demand distinct names (let, var, const) instead of a parameterized construct. In every case the choice was conceptual clarity over surface economy: more symbols, but each one is a signal.

Next: 02 · Memory