Pillar 04 · The polish

Abstraction

Say what you mean with a small, orthogonal core: pattern matching instead of switch ladders, Result instead of thrown exceptions, named records instead of tuples. One good way per concept — not TypeScript's accreted layers.

Too many ways to say the same thing

TypeScript grew by accretion, and it kept every layer. You can model a fixed set of cases as an enum, a union of string literals, or a const object. You can describe a shape with an interface, a type, or a class. There are namespaces and modules, decorators and higher-order functions, five idioms for nearly everything — each with its own edge cases.

For a human that's flexibility. For an agent it's a minefield: more ways to express a thing means more ways to pick the one with the footgun, and more idioms to keep straight while doing it. Glyph's abstraction pillar is a deliberate subtraction — one sharp tool per job, chosen so the obvious way is also the safe way.

01 match is the only conditional

There is no if/else statement. Branching is match, everywhere, and match is an expression — it produces a value. One construct covers a boolean test, a union dispatch, and a value table, so there's no decision to make about which branching form to reach for.

TypeScript — statement, not a value grade.ts
let label: string;
if (score > 90) label = "A";
else if (score > 80) label = "B";
else label = "C";
Glyph — one expression grade.glyph
let label = match score > 90 {
  true => "A",
  false => match score > 80 {
    true => "B",
    else => "C",
  },
}

02 Tagged unions and pattern matching

The way to model "one of these shapes" is a tagged union, and the way to take it apart is match, which destructures the payload in the same step it selects the case. No discriminant field to hand-maintain, no switch ladder on a string tag, no cast inside each branch to recover the shape.

feed.glyph
type Feed =
  | Loading
  | Loaded(Array<Post>)
  | Failed(string)

fn view(feed: Feed) -> string {
  return match feed {
    Loading => "...",
    Loaded(posts) => render(posts),   // posts is Array<Post> here
    Failed(msg) => msg,               // msg is string here
  }
}

03 Result over exceptions

Failure is a value, not a hidden control-flow jump. A function that can fail says so in its return type — Result<T, E> — and you compose failures with the ? operator instead of wrapping call sites in try/catch and hoping the right thing throws the right class. The error path is as visible as the happy path.

04 Named records over positional tuples

Data is described with named fields, always. There's no object-literal shorthand and no reaching for an anonymous tuple whose [0] and [1] you have to remember. The shape reads the same at the definition, the construction, and the use.

TypeScript — what are [0] and [1]? point.ts
function origin(): [number, number] {
  return [0, 0];
}
const p = origin();
p[0]; p[1];  // x? y? row? col?
Glyph — the names travel with the value point.glyph
type Point = { x: number, y: number }

fn origin() -> Point {
  return { x: 0, y: 0 }
}

05 One primitive per job

When a concept genuinely needs its own tool, Glyph gives it exactly one. Shared mutable state lives in a std/store. A file or socket handle that must be closed once is an owned resource. Iteration is for x in xs. Each is a single, named, purpose-built primitive — not a pattern you re-derive from a grab-bag of general parts and get subtly wrong.

Abstraction here means fewer, sharper tools — not more. Every concept has an obvious form, and the obvious form is the one you're meant to use.

The benchmark — coming

The way to measure this is surface area: how many distinct concepts a reader has to hold to understand a program, and how much code it takes to express a common task cleanly. Glyph isn't in real-world use yet, so the chart below is a placeholder that fills in with real measurements once there's a body of real programs to compare.

Distinct concepts to read a typical module · lower is better

TypeScript vs Glyph

Glyph
pending
TypeScript
pending
Not measured yet We'll publish concept-count and lines-to-express-X comparisons from real programs here as soon as there are enough to measure honestly.

A smaller core is easier for a person to learn once and easier for a model to get right every time. That's the whole polish layer in one sentence: take away the choices that only added risk, and what's left is a language that says what it means.