A strongly typed, concatenative, stack-based language — a textual
syntax for cartesian string diagrams. Programs are wiring diagrams:
juxtaposition is parallel wires, >> (or ;, or a newline) is
composition, and the type system infers a principal type for every
diagram with no annotations, ever.
The design bet: keep the primitive set tiny (~40 morphisms) and prove
it spans everything else in the language itself. The entire
standard library is derived user code: booleans, comparisons, while
and until, the list type and its library, the sum monad,
conditionals and guard ladders, data-type folds — and the
metaprogramming layer, where reflected code is a list you munge with
the same library.
# scores travel as a bare bundle; the syllabus is a dot product;
# the letter is a decision ladder; fairness is a law you can run
def dot = zipN ; [(acc a b -> (a b ; *) acc ; +)] 0 ... ; foldExp2
def letter =
p ->
(89 p ; less) "A" ...
(79 p ; less) "B" ...
(69 p ; less) "C" ...
"F" ...
decide
95 90 95 ; weighted ; letter ; print # A — and dot : Intⁿ Intⁿ ⇒ Int
# was inferred, not written
No local GHC needed — a Docker one-liner:
docker run --rm -it -v "$PWD":/w -w /w haskell:9.4-slim \
sh -c "cabal build exe:braid && cabal run -v0 braid -- examples/registrar.braid"braid <file> runs a file; braid alone opens the REPL:
braid> :t true
true : • ⇒ Bool
braid> :t dupN
dupN : a0ⁿ⁰ ⇒ a0ⁿ⁰ a0ⁿ⁰
braid> 7 >> [_ 100 >> less?] [2 _ >> *] ... >> while
stack: 112 : Int
braid> :doc decide
## fold a product of decisions accumulated line by line with `...`
:t shows a type (:t! raw, un-folded); :doc and :defs browse
the prelude; every REPL line runs against a persistent typed stack.
- Everything exact. Constants are maps from nothing (
1 : • ⇒ Int), operations consume exactly their inputs (+ : Int Int ⇒ Int). Passing other wires along is always explicit:1 ... >> +is increment. No implicit anything — this is what keeps inference principal. - Sums are alternate flows.
(Int | Str)is one wire carrying either. Rows(f | g)run one branch;mergerejoins;assocL/assocRre-nest;case(a, b, c)folds a whole nested sum at once. Bool is just(• | •). - Predicates are routers.
odd? : Int ⇒ (Int | Int)routes its input instead of returning a detached boolean — branches receive the data. Drop the?to forget instead:odd : Int ⇒ Bool. - Failure is a track.
>=>composes hit-tracks and lets misses fall through;>?>chains along the miss track (it is elif);readFileandparseare railway stages too. None of them are primitive: each is>> (stage | injector) >> merge, bundled. - Guard ladders are ordinary words. Bind the subject and a guard
is a bare Bool beside its answer;
...accumulates one lane per line anddecidefolds the product — first true lane wins:Guards-as-data variants (x -> (x >> negative) "neg" ... (x >> zero) "zero" ... (x >> toStr) ... decidefirstTrue, clause ladders +choose,if/elif/elsefold-as-you-go) are all prelude defs. No guard syntax exists in the parser. - Loops are values.
loopis Elgot iteration;whileanduntilare three-line prelude defs; general recursion usesrecursewith a placement discipline. - Data types are declared sums.
data Tree(a) = (a | Tree(a) Tree(a))— the name rolls,unTreeunrolls (both free at runtime), andfoldTreeis generated: elimination by points. - The list defines itself.
type List(a) = (• | a List(a))in the prelude; literals,map,fold,filterare all derived. - Widths are exponents.
Intⁿ(typedInt^n) is n wires — a vector with no box, an exponential object with finite base. One fold word is variadic over bare stack products (sumN : Intⁿ ⇒ Int); the GLA generators are width-polymorphic (addN : Intⁿ Intⁿ ⇒ Intⁿ,zipN : aⁿ bⁿ ⇒ (a b)ⁿ); a matrix is a valueFn⟨Intⁿ ⇒ Intᵐ⟩; and a 3-weight syllabus cannot meet a 4-score transcript — dimension errors are type errors.nis erased: the running stack's width is its own witness. - Laws are programs. Associativity, De Morgan, dot-product
symmetry, the copy/add bialgebra — they run as code
(
examples/laws.braid,gla.braid,registrar.braid), and they're operational: a checked law is a license to rewrite (contrapose a chain, parallelize a fold, flip a weighting). - Code is data.
reflectturns a quotation into its spine — a list of stages of atoms — sotake/map/reverseslice and transform programs;evalCoderuns them (dynamically checked, failures on the miss track);unparse/parseandreadFile/writeFileround-trip code through disk. The graphical-linear-algebra transpose isreverse >> map dualize.
examples/ is the guided tour: start with fizzbuzz, validate
(railway), ladder (every guard idiom), iterate (while), then nat
and tree (data types and folds), lists, conditionals and case
(rows, deferred sums, case(…)), sniff (typed CSV-cell refinement),
sac (split-apply-combine), laws, parallel, matrices, gla
(bundles and the bialgebra), code, transpose, io — and finish
with registrar, which uses most of the language in forty lines about
grade school.
A design-driven prototype: one Haskell module for the whole language
(typechecker, interpreter, REPL), a 420+ case test suite, a full
reference (MANUAL.md — every feature, with checker-verified types),
and design notes recording each decision and the theorems that forced
it —
expanded-spec.md, spec-sums.md, spec-code.md,
design-control-flow.md (why guard syntax kept getting built and torn
out, and what replaced it), design-exponents.md (dimension-indexed
segments: why exponents not stars, why unary successors suffice, why
the eliminator is a fold and not an unroll). Deliberately absent so
far: floats, modules beyond the auto-loaded prelude, typed splicing,
labeled record fields, effect types.