Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Data.Name

Deterministic nominal identities with phantom namespace brands.

A name’s semantic identity is one integer, drawn from Control.Fresh’s monotonic counter, so a run’s names are a pure function of allocation order: same program, same names, on every backend and every machine. The source spelling travels beside the identity as a presentation hint and carries no meaning; renaming a binder’s hint changes what a diagnostic prints and nothing else.

The phantom space parameter brands a name with its namespace. A consumer declares one marker type per namespace (terms, types, modules) and the checker refuses to mix them: Name(TermSpace) and Name(TySpace) are distinct types with no conversion. The brand is compile-time only; every name is one integer at runtime.

This is nominal identity, not a substitution framework. Once binders and references carry unique identities, substitution compares names directly and can never capture a same-spelled inner binder; disambiguating suffixes become the pretty-printer’s concern alone. The equality and ordering instances are hand-written on the payload so the brand is never constrained: a marker type needs no instances to brand a name. Opt-in: not in Base.

Types

Name

newtype Name(space) = Name(Int)

A branded nominal identity: one integer, one phantom namespace.

Binder

type Binder(space) = Binder { name: Name(space), hint: String }

A binder: the identity the checker compares, and the source spelling diagnostics print. Equality, ordering, and display are written by hand below, on the identity alone: a derived structural pair would let the diagnostic hint decide whether two binders are the same.

Instances

eqName

instance eqName : Eq(Name(space))

ordName

instance ordName : Ord(Name(space))

showName

instance showName : Show(Name(space))

eqBinder

instance eqBinder : Eq(Binder(space))

ordBinder

instance ordBinder : Ord(Binder(space))

showBinder

instance showBinder : Show(Binder(space))

Functions and Values

name_id

name_id : forall a. (Data.Name.Name(a)) -> Int

The integer identity a name carries. Serialization and hashing commit to this and the surrounding schema, never to an address or a random seed.

fresh_binder

fresh_binder : forall a. (String) -> Data.Name.Binder(a) ! {Control.Fresh.Fresh}

Allocate a binder with a fresh deterministic identity and the given presentation hint.

run_fresh(\() -> [render_binder(fresh_binder("x")), render_binder(fresh_binder("x"))])
[x%0, x%1]

bound_name

bound_name : forall a. (Data.Name.Binder(a)) -> Data.Name.Name(a)

A reference to a binder: the branded name the checker compares.

render_binder

render_binder : forall a. (Data.Name.Binder(a)) -> String

Render a binder as its hint plus its identity, deterministic and unique by construction. A prettier printer may use the hint alone where no sibling shares the spelling.