Guji 2026
Statically typed and functional-first, compiled to native code - optimizes for text processing, with first-class regex and grammars.
Influenced by: Go OCaml Raku Rust Perl
Guji is a compiled, statically-typed, functional-first language whose signature feature is first-class text processing: regular expressions (/\w+/) and PEG grammars (grammar … { rule … }) are part of the language itself, not a library. As of v0.1-alpha it ships two engines that are kept byte-identical by a differential test gate: a reference tree-walking interpreter (guji file.guji) and a native ahead-of-time compiler (guji build emits a single self-contained executable). Bindings are immutable by default and locally inferred, every value carries a Perl-style sigil ($ scalar, @ list, % map), real Platform IO is built in (print, note, read_file, stdin, args, exit), and lightweight concurrency uses hatch tasks over immutable typed channels (Chan[T]). Its guiding rule is "one obvious way" - the language deliberately omits redundant syntax so each task has exactly one idiomatic form.
What makes it distinctive
- Two engines kept in lockstep: a reference tree-walking interpreter (
guji file.guji) and a native ahead-of-time compiler (guji build) that emits a single self-contained executable, validated byte-identical by a differential gate of 319 fixtures. - Text processing is a first-class language primitive, not a library: regex literals (
/\w+/) and PEG grammars (grammar … { rule … }) are built-in types alongsideIntandList. - A two-layer text model with a clear division of labour: flat regex for non-recursive matching (named captures read as
$m<name>, the compiler rejects regex recursion), and ordered-choice PEG grammars whoseparsereturns anOption[Bush]parse tree for recursive, structured input. - Real Platform IO built in (§15.4):
printto stdout,noteto stderr,args()for the command line,read_file/open/stdinreturning handles whose.slurp()and.lines()read whole files or chomped line streams, andexit($code): Never. - Immutable by default: bindings can't be reassigned without
mut, methods that 'modify' return a new instance, and everything that produces a value is an expression. - Perl/Raku-style sigils are part of every name and invariant:
$scalar,@list,%map - and class fields use twigils for visibility ($.public,$!private). - No exceptions -
Option[T]/Result[T, E]plus the postfix?propagation operator, withmatchexhaustively checked so the compiler names any missing case. - Statically typed with pervasive local inference (function return types are inferred too); only exported
pubdeclarations must annotate their interface, and generics run through subs, classes, and enums (sub id[T],Stack[T]). - Live Go-style CSP concurrency:
hatchspawns a task,channel()builds a typedChan[T],send/recv/closeandfor $x in $cdrive it, and because every value crossing a channel is immutable, no data race is even expressible. - Data-first uniform call syntax:
$x.f($a)is exactlyf($x, $a), so every function chains left-to-right with.and there is no separate pipeline operator; 'one obvious way' omits redundant syntax on purpose.
History
Guji is an in-house language designed in 2026 around a single, opinionated thesis: that text - matching it, parsing it, transforming it - is a primary concern of programming and therefore belongs in the language rather than in a library. Where most languages bolt regular expressions on as string methods and push real parsing into external parser generators, Guji makes regex literals and PEG grammars built-in types alongside Int, List, and Map. The specification (guji-spec.md) is the single source of truth, and the reference implementation is written in Go.
The design rests on five principles. One obvious way: for any task there is exactly one idiomatic construct, and overlapping or redundant syntax is omitted on purpose. Functional-first: bindings are immutable by default, data is transformed rather than mutated, functions are first-class values, and control constructs (if, match, blocks) are expressions that yield values. Inferred static types: every binding has a type known at compile time, but annotations are rarely required - local inference fills them in, function return types are inferred, and only exported pub declarations must annotate their interface. Text as a first-class concern: regexes and grammars are the language's signature capability. Compiles to a single binary: alongside the reference interpreter, the native compiler produces one self-contained executable with no external runtime to install.
The surface syntax visibly draws on Raku (formerly Perl 6): every binding wears an invariant sigil that declares its shape ($count, @items, %ages), class fields use Raku-style twigils for visibility ($.public, $!private), topic lambdas use the implicit $_ topic variable, and - most tellingly - grammars are a named, reusable, structured form of pattern, exactly the role grammars play in Raku. Guji even permits emoji as whole identifiers in the snake_case class, with the sigil keeping $🚀 unambiguous.
From the ML tradition (OCaml) Guji takes its functional-first stance: local type inference, sum types via enum, exhaustively-checked match as the way to take values apart, and immutable bindings as the default rather than the exception. From Rust it borrows the no-exceptions error model - Option[T] and Result[T, E] as ordinary enums, the postfix ? operator that propagates None/Err to the caller, exhaustiveness checking that names the missing cases, and panic (returning the bottom type Never) reserved strictly for unrecoverable bugs. Generics run throughout: subs, classes, and enums can be parameterised (sub id[T]($x: T): T, Stack[T]).
The concurrency design follows Go's CSP model and is live, not reserved: lightweight tasks are started with hatch { … }, typed channels (Chan[T]) are created with channel() and driven with $c.send(x), $c.recv() (yielding Option[T]), and $c.close(), a for $x in $c { … } loop drains a channel until it closes, and a select statement waits on several channel operations. The Guji twist that the immutability story makes free: every value crossing a channel is immutable, so tasks share data only by communicating and the language structurally cannot have a data race. The data-first uniform call convention ($x.f($y) is exactly f($x, $y)) and the single-self-contained-binary deployment model are also Go-flavoured, as is the Platform IO surface (§15.4): print to stdout and note to stderr, args() for the command line, read_file/open/stdin returning handles whose .slurp() and .lines() read whole files or chomped line streams, and exit($code) returning Never.
The two text layers are deliberately complementary. Regular expressions (§13) handle flat, non-recursive matching: Unicode-aware shorthand classes, named captures returning Option[Str] read as $m<name>, the ~~ match operator yielding Option[Match], dynamic construction via Regex.compile (returning Result[Regex, Str]), substitution with .replace, and <{ … }> splicing to compose Regex values. The compiler explicitly rejects regex recursion and conditionals, pointing the programmer at grammars instead. Grammars (§14) are the recursive, structured layer: ordered-choice PEG parsers built from token, rule, and regex productions, with a TOP entry point, whose parse returns an Option[Bush] parse tree rather than flat text - a grammar is a pure recognizer, and semantic processing is a separate match pass over the Bush.
The v0.1-alpha milestone is the point at which the language stopped being just the reference oracle: the tree-walking interpreter remains the source of truth, but the native AOT compiler now emits a real binary and is validated against it fixture-by-fixture. A differential gate of 319 fixtures (with sanitizer-clean native builds) requires the native binary's stdout and exit code to match the interpreter exactly, so the two engines stay byte-identical as the language grows.