{
    "archive_path": "archive/1792879566.161403",
    "base_url": "aardappel.github.io/goose",
    "basename": "",
    "bookmarked_date": "2026-10-24 22:06",
    "canonical": {
        "archive_org_path": "https://web.archive.org/web/aardappel.github.io/goose",
        "dom_path": "output.html",
        "favicon_path": "favicon.ico",
        "git_path": "git/",
        "google_favicon_path": "https://www.google.com/s2/favicons?domain=aardappel.github.io",
        "headers_path": "headers.json",
        "htmltotext_path": "htmltotext.txt",
        "index_path": "index.html",
        "media_path": "media/",
        "mercury_path": "mercury/content.html",
        "pdf_path": "output.pdf",
        "readability_path": "readability/content.html",
        "screenshot_path": "screenshot.png",
        "singlefile_path": "singlefile.html",
        "warc_path": "warc/",
        "wget_path": "aardappel.github.io/goose/index.html"
    },
    "domain": "aardappel.github.io",
    "extension": "",
    "hash": "1MYBPFAKB95VVDN1SG36",
    "history": {
        "archive_org": [],
        "dom": [],
        "favicon": [
            {
                "cmd": [
                    "curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--max-time",
                    "120",
                    "--output",
                    "favicon.ico",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.4 (+https://github.com/ArchiveBox/ArchiveBox/) curl/curl 8.14.1 (x86_64-pc-linux-gnu)",
                    "https://www.google.com/s2/favicons?domain=aardappel.github.io"
                ],
                "cmd_version": "curl 8.14.1 (x86_64-pc-linux-gnu)",
                "end_ts": "2026-10-24T22:11:05.638439+00:00",
                "index_texts": null,
                "output": "favicon.ico",
                "pwd": "/data/archive/1792879566.161403",
                "schema": "ArchiveResult",
                "start_ts": "2026-10-24T22:11:05.158552+00:00",
                "status": "succeeded"
            }
        ],
        "git": [],
        "headers": [
            {
                "cmd": [
                    "curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--head",
                    "--max-time",
                    "120",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.4 (+https://github.com/ArchiveBox/ArchiveBox/) curl/curl 8.14.1 (x86_64-pc-linux-gnu)",
                    "https://aardappel.github.io/goose/"
                ],
                "cmd_version": "curl 8.14.1 (x86_64-pc-linux-gnu)",
                "end_ts": "2026-10-24T22:11:05.912181+00:00",
                "index_texts": null,
                "output": "headers.json",
                "pwd": "/data/archive/1792879566.161403",
                "schema": "ArchiveResult",
                "start_ts": "2026-10-24T22:11:05.647992+00:00",
                "status": "succeeded"
            }
        ],
        "htmltotext": [],
        "media": [],
        "mercury": [
            {
                "cmd": [
                    "/app/node_modules/@postlight/parser/cli.js",
                    "https://aardappel.github.io/goose/"
                ],
                "cmd_version": "1.0.0",
                "end_ts": "2026-10-24T22:11:13.770851+00:00",
                "index_texts": null,
                "output": "mercury",
                "pwd": "/data/archive/1792879566.161403",
                "schema": "ArchiveResult",
                "start_ts": "2026-10-24T22:11:09.341735+00:00",
                "status": "succeeded"
            }
        ],
        "pdf": [],
        "readability": [
            {
                "cmd": [
                    "/app/node_modules/readability-extractor/readability-extractor",
                    "/tmp/tmpilcc6d9q",
                    "https://aardappel.github.io/goose/"
                ],
                "cmd_version": "0.0.11",
                "end_ts": "2026-10-24T22:11:09.332855+00:00",
                "index_texts": [
                    "The Goose Programming\nLanguage\nA memory-safe systems language with no heap allocator, garbage\ncollector, or lifetime annotations. In this repository's benchmarks,\nGoose runs faster than C++ and Rust while using less memory.\nTutorial \u00b7 Reference \u00b7 Specification \u00b7 Packages \u00b7 Samples \u00b7 Benchmarks \u00b7 Standard library\nGoose has syntax familiar to C and Rust programmers, but\nthere is no heap. Every dynamic value lives inline on a\ndata stack managed by the compiler. Growth advances a pointer, and\nmemory is freed when its scope ends. The language's type and lifetime\nrules make this memory model safe to use.\n \nWhy Goose\n\nFaster than C++ and than safe Rust, while memory\nsafe. Over sixteen benchmarks Goose runs at 3.3x the speed of\nidiomatic C++, 1.16x hand-optimized C++ and 1.12x the best safe Rust, on\n1.9x, 1.3x and 1.2x less memory (results). These gains come from Goose's\ndata layouts and memory model.\nNo allocator, no GC, no reference counting, no\ndestructors. Memory is a handful of data stacks that the\ncompiler assigns statically. Freeing a million-element structure is one\nstore, however deeply it nests.\nGrowth preserves references. A reference into a\ngrowing array stays valid for as long as the array does. You keep typed\nreferences where C++ must reserve and safe Rust uses\nu32 indices in the corresponding benchmarks.\nMemory safe with zero annotations. No lifetime\nsyntax, no aliasing or exclusivity rules, no unsafe. The\ncompiler infers what every reference is rooted in and rejects references\nthat could outlive their owners.\nNested data stays inline. A string, an array of\nstrings, a record with variable-size fields and an array of those\nrecords are each one contiguous block with no pointer in it. A record\nthat is 160 bytes and an allocation in C++ is 29 bytes and none in\nGoose.\nEnums that cost what they hold. Variable-mode ADTs\ngive each value its own variant's size rather than the largest one's: 4x\nless memory and 2x the speed of a Rust enum on the\nbenchmark that exercises it.\nLinks narrower than pointers. A relative reference\nstores a typed, checked link as a 1, 2 or 4-byte offset. Structures\nbuilt from them are position independent, so your data structure is\nalready its file format: saving is a write, loading is a read plus a\nverification pass that rejects hostile bytes.\nEverything is built in place, guaranteed. A value\nis constructed at its final destination through any depth of calls.\nitems.push(parse(line)) writes the parsed record straight\ninto the array, and returning a growable array by value costs\nnothing.\nReturns across multiple calls.\nreturn err from load returns from a function any number of\nframes up, statically checked, with no unwinder, no Result\ntype and no ? on every call.\nThreads that share nothing. A worker is compiled as\na separate program with its own memory, and flat values cross typed\nqueues as a memcpy. Data races, locks, atomics and memory\norderings do not exist in the language.\nGenerics and higher-order functions with no\noverhead. An untyped parameter is generic. Function values are\ncompile-time entities, so xs.filter() { it > 0 }\ncompiles to the loop it looks like and builds its result straight into\nits destination.\nPlain C in, plain C out. Goose compiles to one C\nfile, so it runs wherever a C compiler does and calls C directly through\nextern fn. The bundled TinyCC backend compiles and runs a\nprogram in-process, with no build step.\n\nThe tutorial walks through all of\nthis by example, the reference covers\nthe whole language topic by topic, the specification has the exact rules, and\nthe benchmarks have the numbers, losses\nincluded.\nFeatures\nThis section focuses on what distinguishes Goose. The tutorial explains these features in more\ndetail, and the samples show them in\nthirty-one complete programs.\nOne memory\nmodel: stacks, and scope exit is the free\nA program has the native call stack, static data and N data stacks,\nwhere the compiler works out N. A data stack is a large address-space\nreservation with a bump pointer, and there is no other memory. At most\none resizable value is live per stack and it is always on top, so growth\nnever moves anything and never checks a capacity. All of this is proved\nat compile time; the runtime tracks only the bump pointers.\nfor round in 3 {\n    var scratch: u8[>..] = [];                        // grow-only: growth is a pointer bump\n    for i in 100000 { scratch.push((i % 256) as! u8); }\n    print(\"round \", round, \": \", scratch.len, \" bytes\");\n}                                                     // the free: one store to the stack top\nNothing moves, so\nreferences survive growth\nstruct Item { id: i32, weight: f32 }\n\nvar items: Item[>..] = [];\nlet first .= items.push(Item { 1, 0.5 });             // a reference to element 0\nfor i: i32 in 2..1000001 { items.push(Item { i, 0.0 }); }\nfirst.weight = 99.5;                                  // still valid, a million pushes later\npush returns a reference to the new element, so a\nprogram can link elements while building the array. A\nvector<T> or Vec<T> may reallocate\nand cannot provide this guarantee. Stable references account for several\nbenchmark gains.\nSafe references, no\nannotations\nEvery reference and slice has a static root: the variable\nthat bounds its target's lifetime. A reference must not outlive the\nvariable that owns its target or access that target through an\nincompatible type. The compiler infers roots and specializes functions\nfor them, so no annotations are needed. There are no aliasing or\nexclusivity rules. When a lifetime check fails, the error names the\nreference's root and its destination:\nfn longest(a: u8[:], b: u8[:]) -> u8[:] { if a.len >= b.len { a } else { b } }\n\nvar outer: u8[>..] = [];\nvar w = outer[..];\n{\n    var inner: u8[>..] = [];\n    format(inner, \"inner text\");\n    w = longest(outer, inner);     // error: storing a reference rooted at inner,\n}                                  //        which does not outlive the destination (\u00a79.2)\nA slice T[:] lets a function accept a range from any\narray kind without copying its elements. For example, split\nreturns slices into its input, and a dictionary keyed by\nu8[:] stores views of strings rather than copies.\nNested data stays inline\nThere is no single array type. There is a family that differs only in\nwhat happens to the size, and every member is\n[metadata][elements...], inline and packed, never a pointer\nto an element block:\n\n\n\nSpelling\nWhat it is\n\n\n\n\nT[k]\nfixed size, known at compile time\n\n\nT[], T[varint]\nsized at construction, frozen after; a fixed or variable-width\nlength\n\n\nT[..k], T[..]\ncapacity inline; grows and shrinks within it\n\n\nT[>..]\ngrow-only: the workhorse; references into it stay\nvalid\n\n\nT[>..<]\ngrow-shrink: stacks, queues, heaps\n\n\n\nStrings are just u8 arrays: u8[>..] is a\nbuilder, u8[] a finished string stored inline in whatever\nholds it, u8[..16] a small string inside a struct,\nu8[:] a view. A struct may contain variable-size parts, and\nthey sit inline in declaration order, so a record is a run of bytes with\nnothing indirect in it:\nstruct Item  { sku: u8[varint], qty: varint, cents: varint }\nstruct Order { id: varint, customer: u8[varint], items: Item[varint] }\n\nvar book: Order[>..] = [];\nbook.push(Order { id: 1001, customer: \"alice\",\n                  items: [Item { sku: \"SKU-441\", qty: 2, cents: 1999 },\n                          Item { sku: \"SKU-7\", qty: 1, cents: 500 }] });\nThat order is 29 bytes and 0 allocations. As C++\nstd::string + std::vector<Item> it is\n160 bytes and 1 allocation, and as Rust String +\nVec<Item> 153 bytes and 4. A whole order book is one\narray that streams through the cache. The price is that an array of\nvariable-size elements is sequential: you can iterate it but not index\nit (unless you keep a second array of references into it as an\nindex).\nEnums in two sizes\nAlgebraic data types are the only dynamic polymorphism: no\ninheritance, no vtables. Every ADT can be stored two ways, chosen at the\npoint of use. Fixed mode (Shape) is a tag plus room for the\nlargest payload, indexable and overwritable. Variable mode\n(Shape..) gives each value exactly its variant's size; the\narray becomes sequential and a value never changes variant, and in\nexchange you may take references into a payload:\nenum Shape { Circle { r: f64 }, Rect { w: f64, h: f64 }, Dot }\n\nvar packed: Shape..[>..] = [];                 // 9, 17 and 1 bytes, not 17 each\npacked.push(Shape.Circle { r: 1.0 });\npacked.push(Shape.Rect { w: 2.0, h: 3.0 });\npacked.push(Shape.Dot);\nfor s in packed {\n    match s { Rect &r => { r.w += 1.0; }, _ => {} }   // edits the payload in place, inside the array\n}\nCase functions provide another way to dispatch on a variant:\ndefine one overload per variant. Calls dispatch on the tag through a\njump table, and the compiler checks that the overloads cover every\nvariant:\nfn area(s: Shape.Circle) -> f64 { 3.14159 * s.r * s.r }\nfn area(s: Shape.Rect) -> f64   { s.w * s.h }\nfn area(s: Shape.Dot) -> f64    { 0.0 }\n\nfor s in packed { print(s, \" area \", area(s)); }\nLinks narrower than pointers\nA T& is a machine address: eight bytes, never\ndangling. A relative reference is the same link stored as a\nnarrow offset: T&<u32> is measured from the field\nitself to a target in the same array, so the structure is position\nindependent, and T&<u32 in pool> from a named\npool's base, so other arrays can link into the pool.\nT&<u32>? uses offset 0 as null. Here is a binary\nsearch tree in one grow-only array, with 12-byte nodes:\nstruct Node { key: i32, left: Node&<u32>?, right: Node&<u32>? }   // 12 bytes, links included\n\nfn insert(pool: Node[>..]&, key: i32) {\n    if pool.len == 0 { pool.push(Node { key: key }); return; }\n    var cur .= pool[0];                             // .= binds a reference; = would copy the node\n    loop {\n        if key == cur.key { return; }\n        let next = if key < cur.key { cur.left } else { cur.right };\n        if next { cur .= next; continue; }          // narrowed by `if`: next is a Node&\n        if key < cur.key { cur.left .= pool.push(Node { key: key }); }\n        else             { cur.right .= pool.push(Node { key: key }); }\n        return;\n    }\n}\nReferences are transparent (no *, no\n->), .= binds or retargets one, and\nNode? is a nullable reference that if,\nguard and assert narrow. The insert is one\npush and one store, and the push cannot invalidate cur.\nWhen lifetimes are not nested, a reusable pool pairs a\ngrow-only array with a hidden freelist: alloc_ref reuses a\nslot or pushes, free hands one back, and since nothing is\never actually freed, a stale slot reads a different value of the\ncorrect type rather than corrupting memory.\nYour data is already a file\nformat\nvar image: u8[>..] = tree.to_bytes();           // a framed byte image of the whole array\nvar loaded, ok = from_bytes<Node[>..]>(image);  // verified before it becomes a value\nimage[image.len - 3] = 200;                     // tamper with a link ...\nvar bad, bok = from_bytes<Node[>..]>(image);    // ... and it is false and an empty array\nThe tree's links are self-relative, so it means the same thing\nwherever it sits: saving is writing its bytes and loading is reading\nthem back, with no serializer, no schema and no pointer fixups.\nfrom_bytes checks the framing, every tag, every length and\nevery link before the bytes become a value, so a corrupt or hostile file\nis a false, never a wild reference.\nBuilt in place, always\nThe copy-free construction guarantee (spec\n\u00a74.3) says a constructed value is always built in its final home,\npropagated top-down through calls. A function returning a grow-only\narray by value writes its elements straight into the caller's variable,\nor into a field of the record being built inside another array, so\nout-parameters mostly do not appear.\nwords.push(str(\"word\", i));                    // formatted straight into the new element\nlet evens = xs.filter() { it % 2 == 0 };       // built straight into `evens`: no temporary\nbook.push(Order { id: 1001, customer: \"alice\", items: parse_items(\"SKU-441:2:1999;SKU-7:1:500\") });\nReturning errors across\ncalls\nstruct User { name: u8[..16], age: i32 }\n\nfn load(text: u8[:]) -> User[>..], u8[] {\n    var users: User[>..] = [];\n    each_split(text, '\\n') { users.push(parse_user(it)); };\n    return users, \"\";\n}\n\nfn parse_user(line: u8[:]) -> User {              // returns a User: no Result, no error parameter\n    let comma = find(line, \",\");\n    guard comma >= 0 else { return [], str(\"expected name,age: \", line) from load; }\n    let age, ok = parse_int(line[comma + 1..]);\n    guard ok else { return [], str(\"bad age: \", line) from load; }\n    let name = trim(line[..comma]);\n    return User { name: name, age: age as i32 };\n}\nreturn E from f returns E as the result of\nthe innermost active call of f, however many frames up, and\nevery function in between keeps its plain signature. It is checked\nstatically, so every call of parse_user must lie inside a\nload call and nothing is uncaught at runtime. It is a\nhidden discriminant checked per frame rather than an unwinder, and the\nmessage is built directly where load's caller wants it. The\nparsers in the samples use it for every syntax error.\nGenerics and function blocks\nfn twice(x) { x + x }                              // an untyped parameter is a generic one\n\nfn each_pair<T, F>(xs: T[:]) {                     // F is a function value: a compile-time entity\n    for i in 0..xs.len - 1 { F(xs[i], xs[i + 1]); }\n}\n\nfn first_gap(xs: i64[:]) -> i64 {\n    each_pair(xs) { a, b => if b - a > 1 { return a; } };   // returns from first_gap, not each_pair\n    return -1;\n}\n\nlet evens = xs.filter() { it % 2 == 0 };\nlet total = fold(xs, 0) { acc, x => acc + x };\nsort(xs) { a, b => a > b };\nEverything is monomorphized. Type arguments are inferred from\nargument types; parameters absent from those types can be supplied\nexplicitly. Function values are passed as generic parameters: every call\nis direct and inlinable, they cannot escape, and there are no closure\nobjects or function pointers, so a higher-order function compiles to\nexactly the loop it looks like. A block may return from its\nlexically enclosing function, and nested functions see the enclosing\nfunction's locals.\n\nthread_fn worker() {                                // a separate program with its own memory\n    loop {\n        let job = qget<Job>();                      // blocks on the typed queue for Job\n        guard job.n >= 0 else { break; }            // -1 means stop\n        qput(Result { n: job.n, digits: count_digits(job.n) });\n    }\n}\n\nfor i in n { ids.push(thread_spawn(worker)); }\nThere is no shared mutable memory. A thread_fn and\neverything it calls is compiled as its own program with its own data\nstacks and its own copies of the globals. Values cross through typed\nqueues, one per type, and must be flat, with no references at\nany depth. That is cheap because a flat Goose value is contiguous: a job\nor a result carries real data, pixels included, and crossing is a\nmemcpy.\nA failed check or abort in a worker started with\nthread_spawn_contained ends only that worker. Its message\ngoes to whoever waits for it, not to stderr, so a supervisor can log it\nand start another:\nvar why: u8[>..] = [];\nwhile !thread_wait(thread_spawn_contained(serve, attempt), why) {\n    log(why);                                       // \"assert failed (serve.goose:12)\"\n    why.clear();\n    attempt++;\n}\nThe first thread_wait(id, msg) for a worker takes the\nmessage; a caller that doesn't ask for it loses it.\nPlain C in, plain C out\nextern fn hypot(x: f64, y: f64) -> f64;             // straight from libm\nextern fn crc32_bytes(s: const u8[:]) -> u32;       // a slice crosses as { data, len }\nextern fn stats_of(xs: i32[:], out: Stats&);        // a struct filled through a pointer\nAn extern fn binds a Goose signature to a C symbol. The\nmath and os modules use this interface. Only\ntypes with supported C representations may cross the boundary;\nunsupported signatures are rejected at the declaration. The compiler\nemits one C file for the whole program. The bundled TinyCC backend can\ncompile and run that C inside the Goose compiler's process.\nWhere the speed comes from\nMuch of the speed comes from the memory model: no heap allocation or\nper-object teardown, contiguous data, narrow links, and enums sized for\nthe variant they contain. Every index is bounds-checked, and the\ncompiler removes checks it can prove unnecessary. An assert\non a slice length often lets a kernel eliminate its remaining checks and\nvectorize. --bce-lines reports the checks that remain.\ncheckfree fn rejects a kernel if any bounds check survives;\nindex types let graph edges retain validation done during construction.\nBenchmark measurements use whole-process wall-clock time, including\nteardown.\n\n\n\nGeometric mean over 16 benchmarks\nvs idiomatic C++\nvs hand-optimized C++\nvs best safe Rust\n\n\n\n\nspeed, MSVC backend\n3.51x\n1.24x\n1.13x\n\n\nspeed, clang backend\n3.54x\n1.26x\n1.19x\n\n\npeak memory\n1.95x less\n1.32x less\n1.25x less\n\n\n\nThe summary explains the gains and\nlosses; results.md has every\nmeasurement, and design.md explains what\nthe suite tests.\nWhat it costs you\n\nYou think about where data lives: who owns this, and how long does\nits scope last. Usually the answer is \"the function that builds it\", and\nthat is free; when it is not, it is a reusable pool.\nRecursive functions cannot keep growable data across a recursive\ncall; scratch ends before the call, and what outlives it is passed in as\na pool.\nArrays of variable-size elements iterate but do not index (short of\nan index of references kept beside them), a fixed-mode enum cannot be\npointed into, and a variable-mode one cannot be overwritten. You choose\nper container.\nNo escaping closures, no function pointers, no dynamic dispatch\nbeyond ADT tags, and whole-program compilation only.\nA slot handed back to a reusable pool and still named\nreads whatever its next owner put there: a logic bug, never memory\ncorruption.\n\nTutorial \u00a718 is\nthe full list.\nInstall a release\nPrebuilt compilers for Windows (x64), Linux (x64) and macOS (Apple\nsilicon and Intel) are on the releases page,\nwith the graphics, audio, ui and physics packages compiled in. Unpack\none anywhere and put its directory on PATH: the compiler finds the\nstandard library, TinyCC's runtime and the packages beside itself, and\nkeeps what it builds under ~/.goose\n(GOOSE_HOME). The archive also holds these docs, the\nsamples and the VS Code extension.\nRunning a program in-process (goose file.goose) needs\nnothing else on Windows. On Linux it needs the C library's headers\n(libc6-dev, or glibc-devel); on macOS, the SDK\nof the Xcode command line tools (xcode-select --install).\ngoose build also needs a C compiler: Visual Studio 2022 or\nlater, or clang, on Windows; gcc or clang elsewhere. The Linux compiler\nneeds glibc 2.35 or later (Ubuntu 22.04, Debian 12), the macOS one macOS\n13.3 or later. macOS keeps a downloaded archive's files from running\nuntil xattr -dr com.apple.quarantine is run on the unpacked\ndirectory.\nBuild and run\nYou need CMake 3.27 or later, a C++20 compiler (MSVC, clang or gcc)\nand Python 3 for the test and sample runners. TinyCC, which the\nin-process backend is built from, is part of the tree. The graphics,\naudio, ui and physics modules are packages in repositories of their own\n(goose_gfx and goose_physics;\ndocs/design/packages.md):\n-DGOOSE_BUILTIN_LIBS=all fetches them at the pinned tags\n(git, at configure time) and compiles them into the compiler, so their\nprograms run with nothing else installed. Without it the compiler builds\nalone, and a program importing one of them has it built into\n~/.goose the first time, which needs git, cmake and a C\ncompiler.\ngit clone https://github.com/aardappel/goose\ncd goose\ncmake -B build -DCMAKE_BUILD_TYPE=Release -DGOOSE_BUILTIN_LIBS=all\ncmake --build build --config Release\nGOOSE_BUILTIN_LIBS can also name some of the packages\n(physics, or gfx, which carries\naudio and ui too). On Linux, SDL needs the X11\nor Wayland development packages (third_party/SDL/docs/README-linux.md);\nwithout them the build leaves the gfx module out and says\nwhat to install.\nThe runner scripts prefer build/Release, then\nbuild/RelWithDebInfo, then build, then\nbuild/Debug. Pass --exe to select a specific\ncompiler; use an explicit Debug executable when testing with compiler\nassertions enabled. The compiler is build/goose\n(build/Release/goose.exe with the Visual Studio generator),\nand it finds the standard library in the source tree it was built from.\nOn Windows 10 version 1903 and later, its embedded manifest selects a\nUTF-8 process code page so compiler paths do not depend on the system\nlocale. Earlier Windows versions retain their legacy code page.\nGenerated native executables retain their own process settings.\nRun a program straight from source, in-process\n(goose run says the same explicitly, and\ngoose check only typechecks):\nbuild/goose samples/01_tour.goose\nOr build an executable, which lands beside the source\n(-o puts it elsewhere). The compiler finds a C compiler on\nthe machine (goose toolchain says which: MSVC or clang on\nWindows, cc, gcc or clang elsewhere; --cc chooses) and\nlinks the generated C with the runtime and whatever native modules the\nprogram uses, graphics included. Programs that use threads need this\nroute, since TinyCC cannot place thread-local storage.\nbuild/goose build samples/01_tour.goose && samples/01_tour\nbuild/goose build samples/27_gfx_cube.goose && samples/27_gfx_cube\n--debug turns on the overflow, range and tag checks in\nthe generated C, each abort naming its source line (and an overflow or a\ncast the values involved), and builds the C with debug information;\n--dry-run prints the commands a build would run, for a\nbuild system of your own; -v prints them as they run.\nIntermediates go under ~/.goose (GOOSE_HOME),\nthe runtime object compiled once per C compiler. Useful flags of both\nforms: -O0/-O1/-O2 set the\ninlining level (a build defaults to -O2, a run to\n-O1), --bce-lines reports the bounds checks\nkept per line, and -DGS_DEBUG=1 is what\n--debug defines. Every resizable value lives on a data\nstack of 2 GB of reserved address space, committed as it is used:\n--stack-reserve 16G changes that for a program,\n--stacks reports how many data stacks the program and each\nof its workers can take and the cap that puts on\nhardware_threads(), and -DGS_STACK_STATS=1\nmakes a program report its actual use as it ends.\nA program with C of its own names it in a sidecar beside the source,\nfoo.build beside foo.goose:\nc_sources = [\"impl.c\"];, with c_include,\nc_defines, c_libs and _windows,\n_linux or _macos variants of each for one\nplatform's part. Both forms build it: goose build links it\nin, and goose run builds it into a library the in-process\nrun loads, so an extern fn reaches it either way. A library\ndoes the same with a sidecar beside its entry (or names the static\nlibrary target of its own CMake project,\ncmake_target = \"x\";, which the compiler builds through\ncmake), and a program takes a library with\nimport name from \"source\"; in its root file: a directory\n(\"./lib\"), or a git repository at a tag or commit\n(\"owner/repo@v1\" on GitHub,\n\"host/owner/repo@ref\"), fetched into ~/.goose\nand built there once per C compiler\n(docs/design/packages.md).\nUnderneath, goose -o tour.c tour.goose writes the C,\nwhich holds the program and the part of the runtime that inlines into\nit; the rest of the runtime (the OS layer, data stack reservation,\nthreads, printing) is one more C file, which\ngoose --emit-runtime goose_runtime.c writes, to be compiled\nonce and linked with every program\n(cc -O2 tour.c goose_runtime.o -o tour -lm -pthread; on\nWindows cl /O2 tour.c goose_runtime.obj). Build the runtime\nwith the compiler and flags the programs use; it takes no\n-D of its own, and a program from another version of the\ncompiler fails to link against it. --standalone writes a C\nfile that holds the whole runtime instead, and builds on its own. A\nprogram using audio, gfx, physics\nor ui also links that module's layer, which\ngoose --audio-link msvc|cc, --gfx-link,\n--physics-link and --ui-link name in a\nresponse file\n(cc cube.c goose_runtime.o -o cube @$(goose --gfx-link cc),\na ui program's before the gfx one it draws through).\ngoose build --dry-run prints all of this for the program at\nhand.\nA program using audio plays procedural mono/stereo PCM\nthrough SDL3 without opening a graphics window. See the audio reference.\nThe test suite and the samples run on Windows, macOS and Linux:\npython test/run_tests.py\npython samples/run_samples.py\nFor editing, the VS Code extension\ngives syntax highlighting, the compiler's language server\n(goose lsp: errors as you type, hover, go to definition,\ncompletion, rename, symbols) and one-key runs through the JIT:\ncode --install-extension vscode/goose-language.vsix\nOther editors can run goose lsp themselves; editors has the configuration for Neovim,\nHelix and Emacs.\nDocumentation\n\nDocumentation index: the map of\neverything below, and the root of the documentation site built from\nthese files (how).\nTutorial: the friendly\nintroduction, by example. Read this first.\nLanguage reference: every feature,\ntopic by topic, with a compiling example for each: the whole language,\nwritten to be read.\nLanguage specification: the exact\nrules, when you want to know why something did not compile.\nSamples: thirty-one complete\nprograms in reading order, from a tour of the language to a JSON parser,\na threaded Mandelbrot, a file tree built from two pools, a spinning cube\non the GPU, thousands of boxes raining into a heap and a to-do list in\nwindows of widgets.\nStandard library: fourteen modules,\nall readable Goose under stdlib/, including\ngfx, graphics on SDL3's GPU API (how\nit is built), physics, rigid body physics on Box3D (how\nit is built), and ui, fonts, text rendering and\ngame HUDs, windows and widgets on Nuklear, drawn through gfx (how\nit is built).\nBuilding programs and using\npackages: goose build, libraries from git repositories\nor directories, a program's own C, and how to make a package of your\nown.\nBenchmarks: the numbers, with the\nfull results and the design behind them.\nImplementation notes: how the\ncompiler works, pass by pass, and how it is\ntested.\n\nStatus\nGoose is new. What exists today is a whole-program compiler of about\nthirty thousand lines of C++, the specification, the standard library,\nthe samples, and a test suite that CI runs on Windows, macOS and Linux\nwith an extra sanitizer job. Deliberately out of scope for now: moves\nfor resizable values, more than one resizable per struct, labeled\nbreak, namespace privacy, and more OS/library access. Open\nitems are tracked in the specification's\nAppendix B.\nLicense\nGoose is licensed under the Apache License,\nVersion 2.0.\nAuthors\nWouter van Oortmerssen: Language Design, Compiler\nDesign, Coding standards. home page,\ntwitter.\nClaude Fable: Compiler implementation, Benchmarking,\nSample & Doc writing.\nHistory & Use of AI\nThis repo was produced almost entirely with AI, from a human language\ndesign. I had designed Goose several years ago, and had started to\nimplement it, but running a game startup (which is built on another\nprogramming language of mine, Lobster) there was no time to\nfinish it. Which was sad, because I knew Goose could do things other\nlanguages can't, and it should exist.\nI had not expected AI to help with this until Fable came out. I\nthought it might be capable enough to do a good job. I made it\nessentially clone the style and structure of my other recent compiler\n(Lobster), which is why if you look at the code, it looks rather similar\nto that. My initial design had left lots of things unspecified, and lots\nof back and forth with Fable made me decide on all of those, and it is\nnow a better language for it.\nIt is also an experiment in using AI to implement a new language,\nrather than reproduce an existing one.\nYou may wonder why I had it work in C++, if clearly I could have used\nany language, like Rust, or my own Lobster, or.. Goose itself (that may\nstill happen). With Fable, it was my observation that it is almost\nequally capable in any language, and the strong guardrails of Rust or\nother languages are not as pressing as they once were. Since the\ncompiler emits C, you likely already need to have a C/C++ compiler\naround, so sticking inside one ecosystem would seem to simplify\ndeployment and adoption. I've also integrated libtcc and who knows what\nother C libraries in the future. And, like I said, to have the compiler\ncode mimic my existing Lobster compiler seemed fun, at least I can read\nit like it is my own."
                ],
                "output": "readability",
                "pwd": "/data/archive/1792879566.161403",
                "schema": "ArchiveResult",
                "start_ts": "2026-10-24T22:11:06.829436+00:00",
                "status": "succeeded"
            }
        ],
        "screenshot": [],
        "singlefile": [],
        "title": [
            {
                "cmd": [
                    "curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--max-time",
                    "120",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.4 (+https://github.com/ArchiveBox/ArchiveBox/) curl/curl 8.14.1 (x86_64-pc-linux-gnu)",
                    "https://aardappel.github.io/goose/"
                ],
                "cmd_version": "curl 8.14.1 (x86_64-pc-linux-gnu)",
                "end_ts": "2026-10-24T22:11:06.823099+00:00",
                "index_texts": null,
                "output": "The Goose Programming Language \u00b7 Goose",
                "pwd": "/data/archive/1792879566.161403",
                "schema": "ArchiveResult",
                "start_ts": "2026-10-24T22:11:06.778022+00:00",
                "status": "succeeded"
            }
        ],
        "wget": [
            {
                "cmd": [
                    "wget",
                    "--no-verbose",
                    "--adjust-extension",
                    "--convert-links",
                    "--force-directories",
                    "--span-hosts",
                    "--no-parent",
                    "-e",
                    "robots=off",
                    "--reject=js,mjs,woff,woff2,ttf,otf,eot,mp4,m4v,mov,webm,ogv,mkv,avi,flv",
                    "--timeout=120",
                    "--restrict-file-names=windows",
                    "--page-requisites",
                    "--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.4 (+https://github.com/ArchiveBox/ArchiveBox/) wget/GNU Wget 1.21.3",
                    "--compression=auto",
                    "--timestamping",
                    "https://aardappel.github.io/goose/"
                ],
                "cmd_version": "GNU Wget 1.21.3",
                "end_ts": "2026-10-24T22:11:06.771944+00:00",
                "index_texts": null,
                "output": "aardappel.github.io/goose/index.html",
                "pwd": "/data/archive/1792879566.161403",
                "schema": "ArchiveResult",
                "start_ts": "2026-10-24T22:11:05.919754+00:00",
                "status": "succeeded"
            }
        ]
    },
    "icons": null,
    "is_archived": true,
    "is_static": false,
    "latest": {
        "archive_org": null,
        "dom": null,
        "favicon": "favicon.ico",
        "git": null,
        "media": null,
        "pdf": null,
        "screenshot": null,
        "singlefile": null,
        "title": "The Goose Programming Language \u00b7 Goose",
        "warc": null,
        "wget": "aardappel.github.io/goose/index.html"
    },
    "link_dir": "/data/archive/1792879566.161403",
    "newest_archive_date": "2026-10-24T22:11:09.341735+00:00",
    "num_failures": 0,
    "num_outputs": 6,
    "oldest_archive_date": "2026-10-24T22:11:05.158552+00:00",
    "path": "/goose/",
    "schema": "Link",
    "scheme": "https",
    "snapshot_id": "ef892b4f-b389-4db8-af43-b22153f81598",
    "sources": [
        "/data/sources/1792879566-import.txt"
    ],
    "tags": null,
    "tags_str": "",
    "timestamp": "1792879566.161403",
    "title": "The Goose Programming Language \u00b7 Goose",
    "updated": "2026-10-24T22:11:05.156948+00:00",
    "updated_date": "2026-10-24 22:11",
    "url": "https://aardappel.github.io/goose/"
}