Bare #lang beagle on .bgl selects the experimental Native Core path; .bgl
is not a neutral container for a later target choice. beagle build --materializer c17|qbe|wasm --out DIR FILE.bgl first freezes module.native-program, then writes only the
selected projection. Materializers are replaceable outputs; a digest identifies
the recorded artifact and inputs but does not grant execution authority.
wasm requires --abi wasm32; it is currently the named
Restricted-C17-to-wasi-clang bootstrap rather than a direct emitter. Its reactor
without an entry exports only _initialize and memory. Each --entry names
one public, parameterless Int function and exports the stable
beagle_wasm_entry_v1__<ns>__<name> : () -> i64 adapter, invoked under
Wasmtime during the build; module_0.wasm.seams records the exact surface. That frozen native program is backend-neutral even though its source
profile is native. Hosted Clojure is explicit
#lang beagle/clj on .bclj; JavaScript and Nix keep their explicit language
paths below.
The same source body, saved as .bclj, .bjs, and .bnix:
(defn even-doubles [xs (List Int)] (List Int)
(->> xs
(filter even?)
(map (fn [n Int] Int (* n 2)))))Each target renders it idiomatically — not transliterated:
;; → Clojure: threading macro and seq fns preserved
(defn even-doubles [xs]
(->> xs (filter even?) (map (fn [n] (* n 2)))))// → JavaScript: array methods + arrow functions
function even_doubles(xs) {
return xs.filter(((_x) => _x % 2 === 0)).map((n) => (n * 2));
}# → Nix: lazy let-bindings and curried lambdas
let
even-doubles = xs: builtins.map (n: (n * 2)) (builtins.filter even_p xs);
in
nullSame logic, three back-ends. even? becomes even_p where the target's
identifiers can't carry a ? — names follow each language's rules, the shape
follows each language's idiom. Static type information erases after doing its
job at check time; an explicitly authored binding constraint remains only as
its target-idiomatic runtime guard.
Types aren't only shapes you declare — they can come from the target itself. A NixOS module, authored against the typed option schema:
#lang beagle/nix
(ns ssh)
(nix/module [config lib pkgs ...]
{:options.myConfig.modules.ssh.enable (lib.mkEnableOption "SSH server")
:config
(lib.mkIf config.myConfig.modules.ssh.enable
{:services.openssh.enable true})})emits:
{ config, lib, pkgs, ... }:
{
options.myConfig.modules.ssh.enable = lib.mkEnableOption "SSH server";
config = lib.mkIf config.myConfig.modules.ssh.enable {
services.openssh.enable = true;
};
}services.openssh.enable is typed Bool, resolved from the schema cache.
Assigning a String fails at check time with file:line:col precision —
before nixos-rebuild is ever invoked. Unknown option paths fail at parse
time; wrong-typed values fail at type-check time.
For a longer .bnix module, see examples/nix-module.bnix; for a whole NixOS
system authored this way, see firn,
which builds from flake.bnix directly.