Suppose a lambda body uses its parameter as a function, as in x 42.
Its previously unknown type must now agree with an arrow. Substitutions
record such discoveries; unification asks which discoveries make two types
agree while leaving as much as possible open. This second layer describes
those operations without examining an expression.
A substitution records discoveries such as “a stands for Nat”. We use a
finite list of replacements, with the first binding for a name taking
precedence. The empty list is the identity substitution; a singleton makes
one replacement. Replacements are simultaneous, not recursively expanded.
A name with no recorded replacement remains a type variable. This lookup
returns the replacement as stored; it does not apply the substitution again
to that replacement.
Applying a substitution writes its replacements into a type. Concrete types
stay unchanged, while arrows and products are rebuilt from their parts.
This operation describes the effect of a discovery before we discuss how
a unification algorithm makes that discovery.
Composition carries discoveries forward: compose s₂ s₁ applies s₁
first and then s₂. Replacements already present in s₁ are rewritten by
s₂; names absent from s₁ can still receive a replacement from s₂.
The free variables of a type tell us which unknowns it mentions. This is
also the information needed for an occurs check: solving a = a → b by
replacing a with a → b cannot produce a finite type. Lists represent
finite sets here; membership, rather than order, matters.
A context mentions the free variables of its visible schemes. Shadowed
entries must not contribute, even if a caller constructed the context
list directly rather than using extend. The helper remembers names
already visited so that only their first bindings count.
Substitution in a scheme must preserve binding. Before substitution, rename
its quantified variables away from the body, the substitution domain, and
all variables in replacement types. Thus substituting a ↦ b in
∀ b, a → b keeps the introduced b free. Bound names may change even for
an identity substitution; schemes should be compared up to renaming when
their meaning, rather than their concrete spelling, is what matters.
Two types unify under a substitution when their rewritten forms agree.
This proposition specifies a successful solution without choosing a search
procedure, a substitution representation normal form, or fresh names.
A most general unifier makes no unnecessary commitments: every other
unifier is obtained by applying a further substitution after it. Compare
substitutions by their effect on types, rather than equality of lists.
This is the declarative contract for the unification procedure to come.