Type Mapping

Every formalang type that crosses the WIT boundary maps to a WIT shape. The full table is below; for the rules governing what may cross, see Boundary Policy.

Internal types (closures, ranges, the bump-allocator's free pointer) live entirely inside the core module and never appear in the WIT file.

Primitives

formalangWIT
I32 / I64s32 / s64
U32 / U64u32 / u64
F32 / F64f32 / f64
Booleanbool
String, Path, Regexstring

Containers

formalangWIT
Optional<T>option<T>
Array<T>list<T>
Dictionary<K, V>list<tuple<K, V>>
named tuple (x: I32, y: I32)record { x: s32, y: s32 }

Aggregates

formalangWIT
IrStructrecord
IrEnum (unit arm)variant arm with no payload
IrEnum (single-payload arm)variant arm with one payload type
IrEnum (multi-field payload)variant arm carrying tuple<T0, T1, …>

Examples

A function with primitives only:

#![allow(unused)]
fn main() {
pub fn id(x: I32) -> I32 { x }
}

emits:

package formawasm:generated;

world component {
  export id: func(x: s32) -> s32;
}

A pub struct:

#![allow(unused)]
fn main() {
pub struct Point { x: I32, y: I32 }
}

emits:

package formawasm:generated;

interface types {
  record point {
    x: s32,
    y: s32,
  }
}

world component {
  use types.{point};
}

An enum with mixed arms:

#![allow(unused)]
fn main() {
pub enum Action {
    reset
    add(value: I32)
    replace(x: I32, y: I32)
}
}

emits:

package formawasm:generated;

interface types {
  variant action {
    reset,
    add(s32),
    replace(tuple<s32, s32>),
  }
}

world component {
  use types.{action};
}

Identifier conversion

formalang identifiers use snake_case or camelCase; WIT identifiers are required to be kebab-case. The backend converts every identifier crossing the boundary:

  • call_hostcall-host
  • Pointpoint
  • MyEnummy-enum

This applies to function names, type names, record field names, and variant arm names. Inside a component, the original identifier is preserved (and visible in the wasm name custom section for debugger tooling); only the boundary view is kebab-cased.

Internal-only types

These never appear in WIT and are documented here only so you know they exist:

TypeInternal representation
Range<T>{ start, end } pair in linear memory
ClosureAfter closure-conversion: (funcref, env_ptr) pair
Anonymous tupleAnonymous record laid out by the layout planner
VtableFlat array of funcref-table indices, one per trait method

If you mark a function pub whose signature mentions any of these as a top-level type, pre-flight rejects it. (Some — like anonymous structs nested inside a pub record's fields — flow through transparently because the layout planner handles them.)