tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

README.md (3125B)


Native JavaScript binding

Node.js uses Koffi to call the Haskell embedding ABI. Use Node.js 22 or newer. The binding is synchronous: reduction and IO block the calling JavaScript thread. Use a Worker or supervised process where appropriate. The browser/WASM adapters have their own execution interfaces.

nix build .#tricu-js .#tricu-js-tests --no-link
JS=$(nix path-info .#tricu-js)
"$JS/bin/arboricx-run" run test/fixtures/id.arboricx id hello
"$JS/bin/arboricx-run" run test/fixtures/true.arboricx true --format bool

Import Library from $JS/lib/src/lib.js:

const library = new Library();
const ctx = library.context();
try {
  const argument = ctx.string('hello\0🌳');
  const alias = ctx.retain(argument);
  ctx.release(argument);
  console.log(ctx.toString(alias));
} finally {
  ctx.close();
}

Library(path) selects an explicit library. Otherwise discovery uses TRICU_LIB or the library installed beside the module. Paths resolve through symlinks, and one library is kept loaded per process. Preserve the complete native runtime closure; the Nix package supplies it automatically.

Contexts and handles are bigint IDs. number accepts exact safe JS integers or bigint in the uint64 range; toNumber returns bigint, including small values. string rejects unpaired surrogates, and strings preserve embedded NULs. bytes/toBytes exchange octet buffers. kind returns 0/1/2 for Leaf/Stem/Fork; child returns an owned handle. apply creates a suspension, and reduce demands its outer constructor. Observation/codecs also demand the parts they observe.

loadBundle(bytes, exportName) verifies a bundle and selects an explicit export. runIO(program) runs the built-in native IO driver and returns {outcome, value, cleanup}. Outcome 0/1/2 means completed/failed/cancelled; value and cleanup are independently owned handles. Failure reasons stay lazy. Ordinary error Results can be completion values. Boundary failures throw TricuError with numeric status and diagnostic text.

Every handle returned by a successful call is owned. Release intermediates in long-lived contexts. close releases all remaining roots and is idempotent; Symbol.dispose also closes. Decoded buffers are copied into JS-owned buffers and the native allocation is freed in finally.

The CLI requires run BUNDLE EXPORT, accepts text arguments, and takes explicit --format string|number|bool|bytes (default string). Bytes render as hex. --io selects IO execution; terminal failure/cancellation exits nonzero with the reason left opaque. -- ends option parsing. Integer arguments are available through the programmatic number method. Explicit formats describe the caller's intended interpretation because tree encodings overlap.

The native bindings use Haskell value/IO semantics. Fuel, manual collection, kernel-root helpers, implicit default-export selection, heuristic type decoding, and the old inspect CLI are retired from this binding. Tests use independent expected values and retain malformed-bundle, missing-export and ownership controls.