A let generalises what is not weak
Every let generalises every variable of its result type that is neither
free in Γ nor weak. A weak variable has one type for the whole unit; there
is no value restriction.
What was decided
- Weakness is a permanent mark on a variable of any sort — type, row or
computation.
Unifier::mark_weaksets it;unitehands it to the survivor, and a variable of any sort unified with a weak one, even one already fixed to a structure, becomes weak, so a later mismatch is still seen; binding a weak variable to a structure makes every variable of the structure weak, with the same source, since each occurs in it after resolution. Nothing unsets it. generalizesubtracts weak variables as it subtracts Γ’s free variables, and the scheme records them (Scheme.weak) instead of quantifying them. A scheme prints them_α, after its binders:∀α. [a: α, b: _β].fv⁺includes them. A pending label read whose target is weak is kept by the boundary that would have closed it as a record, and is closed at the unit’s end (Amendment A of the plan).- A stored scheme marks its residuals, and the next unit re-seeds each as a
fresh weak variable (
reseed_weak, inseed_env), so a residual is never read as a foreign id thatStore::findwould alias. A unit settles itsDefineschemes (settle_weak) before they are stored, so a residual the unit fixed is stored as what it was fixed to. - A mismatch that meets a weak variable, or the type one was fixed to, carries a note saying that the type is shared by every use in its unit and that another use fixed it.
- Two things are weak. A computed index’s container, key and element types
are, from the moment the index is read, whether or not it settles
(the one relation); the result of a
checked boundary is
(a-boundary-is-checked-against-its-type). A scheme that settles a weak variable into a
cycle keeps the cycle:
settle_weaksnapshots it like a quantified one.
Why
A checked boundary is only as good as the type recorded there. With plain
generalisation let doc = from-json … would be ∀α. α, and no use would reach
the door that admits the value; with a weak variable it is _α, one type per
unit, and the door is held to every use.
The value restriction is not needed for this. ML restricts generalisation
because ref [] : ∀α. α list ref would let one cell be written at one type and
read at another. ral has no first-class reference cell, and every other way a
value of a type the program did not decide enters typed code is a boundary,
whose result variables are weak. So the restriction would guard nothing, and
would cost let f = map $g its polymorphism and refuse a module used at two
types by one importer. If ral ever gains a reference cell the restriction comes
back with it; the perimeter test of the boundary slice is what says it has not.
Making the mark permanent is what keeps the verdict independent of statement
order. Were it lifted when an index settled, whether let h = { |c| $c[$i] }
was generic would depend on what earlier statements fixed about $i; with the
mark permanent it depends only on the syntax of the let’s body. The index is
therefore weak even where its target is already a list, so that a helper indexing
a known list and one indexing a not-yet-known one are treated alike.
Rejected
The value restriction. Generalising a boundary’s result, as today.
Refines session-scheme-continuity: a stored scheme is closed up to its weak residuals (schemes-leave-closed).