Start here · ~10 minutes

Your first Glyph program

Install it, scaffold a project, run it, then break it on purpose and read the error the compiler gives you. No prior Glyph knowledge assumed — if you've written a little TypeScript, you'll follow every line.

1 · Install

Glyph ships as a single prebuilt binary. You also need Node with tsx and typescript, which Glyph uses to run and type-check.

terminal
npm install -g @glyphlang/glyph
npm install -g tsx typescript   # run & type-check toolchain
glyph doctor                    # confirms your toolchain is ready

2 · Scaffold a project

glyph init creates a runnable starter — a src/main.glyph, a package.json, and a .gitignore.

terminal
glyph init hello
cd hello

Open src/main.glyph — it's a hello-world:

src/main.glyph
module main

fn main(argv: Array<string>) -> number {
  print("hello from glyph")
  return 0
}

Two things to notice: a program runs its main(argv), and main returns a number — the process exit code.

3 · Run it

terminal
glyph run src/main.glyph
hello from glyph

glyph run type-checked it, compiled it to TypeScript, and executed it. That's the whole loop.

4 · Add something real

Now replace the body with a tiny bit of logic — a task status and a function that labels it. This introduces the two things you'll use constantly: a tagged union (type Status = ...) and match, which is Glyph's only conditional.

src/main.glyph
module main

type Status = Todo | Doing | Done

fn label(s: Status) -> string {
  return match s {
    Todo => "not started",
    Doing => "in progress",
    Done => "finished",
  }
}

fn main(argv: Array<string>) -> number {
  print(label(Done))
  return 0
}
terminal
glyph run src/main.glyph
finished

5 · Break it on purpose

Here's the part that shows you what Glyph is for. Delete the Done arm from the match — the kind of thing an agent does when it adds a case somewhere and forgets to handle it — and run again.

src/main.glyph (with the Done arm deleted)
fn label(s: Status) -> string {
  return match s {
    Todo => "not started",
    Doing => "in progress",
  }
}
$ glyph run src/main.glyph
[E0200] Error: typecheck: non-exhaustive match on `Status`: missing variants `Done`
   ╭─[main:6:10]
   │
 6 │ ╭─▶   return match s {
   ┆ ┆
 9 │ ├─▶   }
   │ ╰───────── missing variants `Done`
   │
   │  Help: Add an arm for each missing variant, or an `else` arm to catch the rest.
───╯

It didn't run. In TypeScript the equivalent switch would compile clean and return undefined at runtime; here the missing case is a compile error with a stable code (E0200), the exact spot, and a one-line fix. Run glyph --explain E0200 any time you want the long version.

6 · Fix it

Put the arm back (or add an else), and it runs again.

terminal
glyph run src/main.glyph
finished

That's the core loop: write, run, and when something's wrong the compiler tells you exactly what and how — before your code ships, not after. From here: