Module Patterns

This is a practical cookbook for writing qip modules.

It also includes the error semantics for deciding whether to return a value, return empty output, or trap. Default recommendation: trap on invalid input or overflow for transformation modules.

Choose A Pattern

Use this quick mapping:

Pattern 1: Scalar Validator (No Output Buffer)

Use when you only need a status code.

Exports:

Do not export output_ptr or output caps.

Host behavior:

Good for:

Pattern 2: Normalizer (UTF-8 -> UTF-8)

Use when you rewrite text and return text.

Exports:

Host behavior:

Good for:

Pattern 3: Binary Transformer (Bytes -> Bytes)

Use for non-text payloads.

Exports:

Host behavior matches Pattern 2, but no UTF-8 assumptions.

Good for:

Pattern 4: Numeric Stream (i32 rows)

Use when you want hex lines from 32-bit values.

Exports:

Semantics:

Error Semantics (Merged)

These are the current semantics in qip.

Contract Errors (Host-side)

Execution fails if required exports are missing for the chosen pattern.

Examples:

Capacity Errors (Host-side)

Execution fails if:

Runtime Trap / Call Error (Module-side)

If module execution traps (or function call fails), the stage fails.

Use trap when invalid input should be a hard failure.

How To Trap

Use these language-specific forms when you want hard failure semantics.

Zig:

if (invalid_input) @trap();

C (Clang/zig cc targeting wasm):

if (invalid_input) __builtin_trap();

WAT:

;; inside a function
(if (local.get $invalid_input)
  (then
    unreachable
  )
)

Soft Failure (Module-side)

Use return values to signal non-fatal failure when that behavior is intentional.

Common options:

Host treats this as successful execution unless a bound/contract check failed.

Empty Output Semantics

If output buffers are exported and run returns 0, output is empty.

Choosing Trap vs Soft Failure

Default to trap, especially for normalizers/transformers where silent drops risk data loss.

Prefer trap when:

Prefer soft failure when:

Implementation Checklist