tricu

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

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:

  1. unwraps ok,
  2. checks hostStringTag,
  3. decodes the canonical string payload.

Implementation reference

Invariants

The reference tests cover these invariants:

  1. Each constructor stores the correct tag and payload.
  2. hostValueTag and hostValuePayload destructure values correctly.
  3. runArboricxToTree always wraps successful raw results as hostTree.
  4. runArboricxToString wraps canonical string results as hostString.
  5. runArboricxToNumber wraps canonical natural-number results as hostNumber.
  6. runArboricxToBool wraps canonical booleans as hostBool.
  7. A typed runner returns errHostCodecFailed when validation fails.
  8. 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.