Repository navigation
v0.22.0
Beagle v0.22.0
Beagle v0.22.0 turns Native Core into a practical substrate for standalone command programs and interactive Wasm engines. It closes module resolution: a require is now served only by sources the invocation names or by a declared source root, in the Racket compiler and the self-hosted compiler alike. It also lands the first compiler-owned semantic-unit reuse seam, while keeping whole-program compilation as the authoritative release path.
Language and typing
- Breaking: module sources are now explicit and closed. A
requireresolves only through sources named by the invocation or a declared--module-root LOGICAL_PREFIX=PHYSICAL_DIRECTORY; ancestor-directory search and flat-basename fallback are gone in both compilers. The root maps a complete namespace path to exactly one file, and namespace-based emission preserves qualified identities across modules.beagle build,beagle check,beagle ast,beagle build --materializer, andbeagle native-exeaccept repeated roots; the self-hosted driver takes the same roots plus repeated--sourcefor an exact closed bundle. - Checker and host-contract precision. Inference now keeps unknown distinct from
Any, preserves fresh structure and invariant/second-order contract context, and numeric refinement returns the declaredNumberunion alias (E003) rather than a mismatched primitive type. JavaScript static members,Atom, nativeMap, browser geometry, and numericMathsurfaces are typed, with number-predicate narrowing kept in parity between compilers.
Native and Wasm
- Wasm entry ABI v1. Repeated
--entryflags now produce distinct, qualifiedbeagle_wasm_entry_v1__<ns>__<name>exports. Resource-bearing entries share an instance arena, expose explicit reset, and retain the zero-import reactor contract. - Deterministic Wasm host I/O. Hosts can feed byte records through the exported environment mailbox and read registered Buffer storage directly from linear memory. Entry results, seams, adapter source, and runtime policy remain bound into the build receipts.
- Native programs can own their operating-system boundary. Typed capabilities now cover bounded filesystem inspection and reads, atomic writes, directory creation, append, wall-clock formatting, sleep, inherited and captured processes, explicit streaming child-process lifecycles, and an exclusive file lease.
host.fs/lock-exclusivetakes a non-blocking whole-file lock the operating system releases on close or on process death, returning a closed result union that carries contention as an error variant instead of an unbounded wait. - Transient vector builders.
transient,conj!, andpersistent!give Native Core a linearTransientVecbuilder that avoids rebuilding a persistent vector per element. The checker proves single ownership — including acrossrecur, where a loop-owned builder must be threaded rather than captured — and the builder lowers through the native, C17, and wasm32 materializers. - Typed JavaScript host surfaces. Static members,
Atomvalues, nativeMapconstruction and contracts, browser geometry, and the numericMathsurface are typed rather than opaque, and number-predicate narrowing agrees between the Racket compiler and the self-hosted compiler. - Native data tooling. Compiler-shipped Beagle libraries add datum reader events, strict JSON and EDN event codecs with structured failures, and structural Nix option-path extraction and suggestion utilities.
- Coherent multi-module native builds. Native lowering now composes complete source bundles, resolves qualified cross-module calls, preserves imported unions and match context, and derives vector-literal types without downstream decoy declarations.
- Command-line executables are substantially more capable.
beagle native-exeaccepts one typed(Vec String)argument vector as its entry boundary, while native output and process primitives support complete command behavior without hosted wrapper logic. - Bounded semantic-unit reuse is proven. The compiler can extract, validate, select, and deterministically reassemble exact typed/native unit payloads across layout-only, private-body, and public-interface mutations, and single-unit typed and native compilers now compile exactly one unit per request against a signature-only typing prelude. This is compiler groundwork, not yet a production incremental cache.
Test infrastructure and gate performance
- The active gate is sharded by scheduling unit.
--shard i/npartitions the heavy-first launch order across CI machines, including phase units from sharded test files, while coverage refusals are checked against the complete expansion. One-test units are counted correctly, including singularraco testsummaries. - The Wasm materializer gate runs by phase. Its nineteen phase units complete independently with per-unit cache identities and deadlines, reducing the measured gate from about 400 seconds unsharded to about 168 seconds at four workers. Per-file concurrency is capped so identical fixture builds do not contend for the machine.
Fixes and correctness
- An unresolvable
requireis rejected instead of silently registering a phantom alias. Previously every qualified call through such an alias typed at an arbitrary type with no diagnostic. A namespace the target's host runtime supplies is still accepted through the typed stdlib catalog or an explicitdeclare-extern. - A closed checked bundle is ordered by logical source id, so its module list, closure digest, and derived source facts are a function of the sources alone — not of argument order, and not of where the checkout lives.
- Module exports resolve before inference runs, inference's unknown is kept distinct from
Anyand neutral in joins, freshly inferred structure is marked effectful and preserved at its expected shape, and invariant and second-order contract contexts survive checking. - Nullable text-index results, imported union-member coercion, zero-variant constructors, callback lowering, scalar conversions, source provenance, root-arena lifetime, and Wasm/POSIX shim separation now fail or lower at their owning boundary.
- JavaScript emission preserves qualified aliases under local shadowing and detects asynchronous work nested inside emitted call and assignment forms.
- Native build reports and semantic identities remain deterministic and fail closed when source, interface, obligation, or payload claims disagree.
Compatibility
This is a pre-1.0 minor release, and module resolution is the change most likely to need action. A build that relied on the compiler finding a provider by walking parent directories or by matching a bare file name must now name that provider in the invocation or declare a --module-root for it; the root maps a complete namespace path to one exact file, with - in a namespace segment read as _ in the file name. A root candidate carries the importing module's own profile extension, so a hosted module cannot reach a Beagle Core (.bgl) provider through a root — compile the Core module to the hosted target instead. An unresolvable require is now an error rather than a silent success.
The Wasm executable surface intentionally replaces the single beagle_wasm_entry_v0 export with qualified v1 entry exports; hosts using the old entry name must migrate. The new native host, transient-vector, and codec surfaces are additive. Whole-program builds remain authoritative, and the semantic-unit seam does not imply a persistent cache or a new artifact format.
Release artifacts
The GitHub release continues to attach the beagle-selfhost stage0 native compiler and its beagle-selfhost.seed-nar-hash. CI retains the byte-for-byte native/Babashka/Racket parity gate over the Fram corpus before those assets are published; under closed module resolution that gate compares the corpus modules whose requires it can serve, and reports each module it skips together with the Core provider that made it unreachable.
Full Changelog: v0.21.1...v0.22.0