Skip to content

Internal Compiler API

Andrew Johnson edited this page Jan 31, 2025 · 30 revisions

If you want to write a plugin for LM, then it may be necessary to interface with compiler objects. These APIs presented here are stable and will be supported by their nominal type signature.

If something is not present here in the type signature, then it may not be stable. For example, destructuring types used in these functions may be unstable.

Type Definitions

Types here should be treated as opaque, even if they are not defined as explicitly opaque.

type Type;
type TContext;

Apply

Simple function application can be fairly complicated when considering all of the various inference rules present in the type system. The "apply" set of functions take function types and argument types to produce return types, providing a unified interface for inference.

let apply(functions-type: Type, arguments-type: Type): Type;

Type Constructors

Ground types have short-hand constructors.

# Ground Types
let t1(tag: CString, p1: Type): Type;
let t2(tag: CString, p1: Type, p2: Type): Type;
let t3(tag: CString, p1: Type, p2: Type, p3: Type): Type;

# Type Variables
let tv(tag: CString): Type;

# Top Type
let ta: Type;

# Conjunction
let $"&&"(lt: Type, rt: Type): Type;

Type Destructors

Ground types have short-hand destructors.

# Get type parameter, starting from left (l1) moving left (l2), (l3), ...
let .l1(tt: Type): Type; # T<a,b,c,d> .l1 = a
let .l2(tt: Type): Type; # T<a,b,c,d> .l2 = b
let .l3(tt: Type): Type; # T<a,b,c,d> .l3 = c
let .l4(tt: Type): Type; # T<a,b,c,d> .l4 = d
let .l(tt: Type, i: USize): Type; # ...

# Get type parameter, starting from right (r1) moving left (r2), (r3), ...
let .r1(tt: Type): Type; # T<a,b,c,d> .r1 = d
let .r2(tt: Type): Type; # T<a,b,c,d> .r2 = c
let .r3(tt: Type): Type; # T<a,b,c,d> .r3 = b
let .r4(tt: Type): Type; # T<a,b,c,d> .r4 = a
let .r(tt: Type, i: USize): Type; # ...

Type Normalization

Types have a normal form with minimal inferable information, and a denormal form with maximal inferable information. Super-normal form refers to the type of a left-hand-side binding such as a variable, which may discard some information.

let normalize(tt: Type): Type;
let denormalize(tt: Type): Type;
let supernormalize(tt: Type): Type;

Unification

Unification is a process by which type information is compared or merged. can-unify is much faster than unify.

let can-unify(supertype: Type, subtype: Type): Bool;
let unify(supertype: Type, subtype: Type): Maybe<TContext>;