host-abi.md (7842B)
tricu Host ABI
For native embedding, see the Haskell C interface. Its context/handle ABI transports runtime trees; the value envelopes below are program-level encodings interpreted by the portable runners.
The portable Arboricx runners in lib/arboricx/arboricx.tri wrap successful
results in a tagged value envelope. Hosts decode the selected canonical payload.
This result envelope is separate from the IO action protocol interpreted by
src/IODriver.hs and from the browser's explicit result codecs.
Outer result protocol
Host ABI runners return the existing tricu result shape from lib/binary.tri:
ok value rest = pair true (pair value rest)
err code rest = pair false (pair code rest)
On success, value is a host ABI value.
On failure, code is a canonical Tree Calculus number. The host may report the numeric code and optionally inspect rest for debugging.
Host ABI value shape
A host ABI value is:
pair tag payload
The tag says how the host should interpret payload.
The ABI envelope tags the outer payload, whose contents retain the canonical/raw Tree Calculus representation for the selected type.
Tags
Tags:
hostTreeTag = 0
hostStringTag = 1
hostNumberTag = 2
hostBoolTag = 3
hostListTag = 4
hostBytesTag = 5
Constructors
The ABI constructors are:
hostTree value
hostString characters
hostNumber n
hostBool b
hostList xs
hostBytes bytes
Each constructor returns:
pair tag payload
Examples:
hostString "hello"
hostNumber 42
hostBool true
hostList [1 2 3]
hostTree (t t t)
Payload conventions
Payloads use existing canonical tricu encodings:
| ABI value | Payload |
|---|---|
hostTree |
arbitrary raw Tree Calculus value |
hostString |
proper list of canonical Unicode scalar values (0..0x10ffff, excluding 0xd800..0xdfff) |
hostNumber |
canonical tricu number |
hostBool |
canonical tricu bool (false = t, true = t t) |
hostList |
canonical tricu list (t empty, pair head tail cons) |
hostBytes |
proper list of canonical naturals in 0..255 |
hostList payloads are raw canonical lists, not lists of host ABI values.
Accessors / matching
Accessors:
hostValueTag hostValue
hostValuePayload hostValue
A host can decode the envelope by destructuring the pair directly, but these helpers make the ABI explicit and testable.
Validation predicates
Typed runners validate that the raw application result can be interpreted as the requested type before wrapping it.
Predicates:
hostNumber? value
hostBool? value
hostList? value
hostString? value
hostBytes? value
These predicates validate canonical tree encodings.
Important ambiguity note:
Tree Calculus encodings overlap. For example, t is also false, 0, and []. Typed runners intentionally interpret values according to the requested type.
Error behavior
Typed ABI runners return an error if the application result does not match the requested type.
Codec error:
errHostCodecFailed = 14
Example:
runArboricxToString bundle args
returns:
ok (hostString resultCharacters) rest
if resultCharacters is string-like, otherwise:
err errHostCodecFailed rest
The typed runner preserves the execution remainder on codec failure. Invalid dispatch tags and improper argument-list spines are rejected before bundle parsing/application, with code 14 and an empty remainder. The raw tree constructor remains unchecked; typed execution checks the selected payload interpretation.
The host string envelope carries Unicode scalar values. Wire manifest text uses validated UTF-8 octets.
Execution wrappers
The base self-hosted Arboricx runners are defined in lib/arboricx/arboricx.tri:
runArboricxArgs bundleBytes args
runArboricxArgsByName nameBytes bundleBytes args
Host ABI wrappers layer typed output envelopes on top:
runArboricxToTree bundleBytes args
runArboricxToString bundleBytes args
runArboricxToNumber bundleBytes args
runArboricxToBool bundleBytes args
runArboricxToList bundleBytes args
runArboricxToBytes bundleBytes args
Named-export variants:
runArboricxByNameToTree nameBytes bundleBytes args
runArboricxByNameToString nameBytes bundleBytes args
runArboricxByNameToNumber nameBytes bundleBytes args
runArboricxByNameToBool nameBytes bundleBytes args
runArboricxByNameToList nameBytes bundleBytes args
runArboricxByNameToBytes nameBytes bundleBytes args
Host usage
For a bundle whose default export is an unapplied function:
append "hello "
A host that expects a string result evaluates:
runArboricxToString bundleBytes ["james"]
On success, the result is:
ok (hostString "hello james") rest
The host then:
- unwraps
ok, - checks
hostStringTag, - decodes the canonical string payload.
Implementation reference
- Tree constructors, numbers, strings, and lists:
src/Runtime.hs - Result protocol:
lib/binary.tri - Arboricx parser/executor:
lib/arboricx/arboricx.tri - Host ABI implementation:
lib/arboricx/arboricx.tri
Invariants
The reference tests cover these invariants:
- Each constructor stores the correct tag and payload.
hostValueTagandhostValuePayloaddestructure values correctly.runArboricxToTreealways wraps successful raw results ashostTree.runArboricxToStringwraps canonical string results ashostString.runArboricxToNumberwraps canonical natural-number results ashostNumber.runArboricxToBoolwraps canonical booleans ashostBool.- A typed runner returns
errHostCodecFailedwhen validation fails. - Named-export typed runners select the requested export before wrapping.
Structured host IO
The typed value envelopes above are independent of the IO execution protocol.
Current hosts execute only the canonical tricuIO envelope with version 2.
Unsupported versions reject before action payloads are demanded. Historical
version-1 trees can be inspected as data; execution requires a retained runtime.
io reexports io. The protocol supplies sequencing, scope-owned resources,
child tasks, consumable joins, supervision and cancellation. Named/versioned
facilities provide binary streams, codecs and backend-specific OS operations.
Native execution uses OS process authority and explicit resource ownership. The IO contract specifies
wire shapes, ordinary Results, terminal outcomes, cancellation and cleanup.
Native Haskell supports filesystem, TCP, process/context and clock facilities. The Haskell browser host supports core/task and terminal operations. Query capabilities for the selected backend before choosing a facility.
Source bytes and publication
compiler_io is the source-owned compiler policy over IO. Whole-source reads
preserve exact raw octets and decode strict Unicode scalars, rejecting malformed
UTF-8 and an initial BOM without normalizing text or newlines. Encoding U+FEFF
is valid; source-file policy rejects only an initial BOM. Whole-source operations
retain their values and enforce a 64-MiB source/metadata budget. Heap usage
requires a separate bound.
Indexed tree transport authenticates bytes and preserves sharing. Source-level storage helpers own sharding, expected-digest checks, no-replace publication and winner verification. File/directory sync follows host filesystem guarantees; a post-publication failure can leave a valid object visible. Scope cleanup releases live resources while the runtime is executing. Process death requires external recovery, and paths resolve under OS process authority. See storage and the IO validation contract.