1.5 Contributions
2.1.5 Type constraints
Colored Local Type Inference infers locally optimal instantiations for all type variables that appear in the inferred polymorphic function types. The inference is a two-stage process - first the inference collects type constraints from subtyping relations, and later it solves them in an attempt to come up with an optimal solution with respect to the result type of the function. The constraint generation technique used in Colored Local Type Inference is a direct trans- lation of the one used in the simple Local Type Inference, as described in Pierce and Turner [2000].
To represent the collected type constraints, Local Type Inference uses so called a-constraint sets. The a-constraint set C is defined by Pierce and Turner [2000] as a set of inequalities {Si<:ai <:Ti}for each of the type variables ai∈ a. Since the inequalities are essentially the
lower and upper type bounds of the type variable, we will use the latter terminology in our discussion.
Chapter 2. Preliminaries
Individual type constraints collected from the subtyping relation<: are in a form of either up- per{a<:R}, or lower{R<:a}type bounds, for some type variable a and type R. The corre- sponding lower⊥ and upper type bounds are added implicitly, respectively. For reference, Figure 2.5 provides a complete set of rules that realize the constraint generation judgment, and that need to be analyzed by any type debugging mechanism if one needs to explain the inferred type bounds.
The constraint generation algorithm for Local Type Inference assumes that (fv(Si)∪fv(Ti))∩ a= , wherefvreturns a set of free variables from a type. The condition ensures that the sub- sequent inference of the type substitution is a matching-modulo-subtyping problem rather than a unification-modulo-subtyping problem which would prevent as from guaranteeing the principality of the inferred types.
For illustration purposes, we present the inference of constraints sets for four subtyping rela- tions:
(1) aI nt→ Int <: a ⇒{I nt→ Int<:a<:}
(2) aI nt→ Int <: a → a ⇒{I nt <:a<:I nt}
(3) a,ba→ b <: (⊥ → ⊥) → Int ⇒{⊥ → ⊥<:a<:,⊥<:b<:I nt}
(4) aa→ Int <: ∀b.b → Int ⇒{<:a<:}
Type constraints for the same type variable are combined using the meet operation. For con- straint sets C and D, their meet, C∧D, calculates least upper bound, ∨, of their lower bounds, and greatest lower bound,∧, of their upper bounds:
{ Si∨Ui<:ai<:Ti∧Vi| Si<:ai<:Ti∈ C and Ui<:ai<:Vi∈ D }
For example, for {I nt → Int <:a <:}⊆ C and{(⊥ → ⊥)<:a <:,⊥<:b <:I nt}⊆ D,
E = C ∧ D is equivalent to{⊥ → Int <:a <:, ⊥<:b <:I nt}. Similarly, the second con- straint set in the above examples results from the approximation of the{I nt <:a<:}and {⊥<:a<:I nt}constraints.
The last generated a-constraint set in the above examples provides a surprising lower type bound for the type variable a, {<:a}, rather than{b <:a}. The former is a result of a variable-elimination-by-promotion/demotion operation which eliminates the occurrences of out-of-scope type variables by substituting them with either the supertypes (promotion, de- noted as⇑) or subtypes (demotion, denoted as ⇓) of the types that are type variable-free, a process that has been formally described in [Pierce and Turner, 2000, Section 3.2].
For example,
• variable-elimination-by-promotion: b ⇑{ b } and b → ⊥ ⇑{ b }⊥ → ⊥. 26
2.1. Colored Local Type Inference
• variable-elimination-by-demotion: b ⇓{ b }⊥ and b → ⊥ ⇓{ b } → ⊥.
The point of using variable elimination is to avoid generating a-constraint sets that have vari- ables in the type bounds that are outside of their scopes.
In practice, such variable-elimination operations do not pose any problems in our debugging techniques; the variable-elimination of the individual type bounds can be delayed based on the position of the out-of-scope type variable until the instance of the type variable is approx- imated or used in some type operation.
An a-type substitutionσC ,R represents an instantiation of type variables inferred from the a-constraint set C with respect to some type R. Formally, theσC ,R type substitution is a fi-
nite map, with a domain that ranges over the set of type variables, i.e., d om(σC ,R)= a. The
domain d om(σC ,R) of a substitutionσC ,R is the set of type variables which do not map to
themselves by the type substitution. The inferred map has to satisfy the individual approxi- mated type bounds for each of the type variables
∀ai. ai∈ a ∧{Si<:ai <:Ti}⊆ C =⇒ Si <:σC ,R(ai)∧ σC ,R(ai) <:Ti
and make the subtyping relation from which the constraints are collected from (i.e., S <:T ) satisfiable
σC ,RS <:σC ,RT
The optimality of the inferredσC ,Rsubstitution is determined in terms of the position of each
of the individual type variables from the a-constraint set with respect to some type R. Pierce and Turner [2000] provides a formal specification of each of the potential positions; we only briefly recall that any type R can be either constant, covariant, contravariant or invariant in some type variable a, where a∈ a. In general the type can be determined to be covariant or contravariant in a type variable by examining whether a type variable occurs to the right or left of an arrow; in the case of function types taking other functions as arguments the rule can be applied recursively.
Since for any a-constraint set there can be many different possible type substitutions satis- fying the above subtyping requirements, the algorithm aims to find a minimal substitution σC ,R. Pierce and Turner [2000] defines the minimal substitutionσC ,Ras follows:
Chapter 2. Preliminaries
For each Si <:ai<:Ti∈ C :
• If R is constant or covariant in ai, thenσC ,R(ai)= Si
• Else if R is contravariant in ai, thenσC ,R(ai)= Ti
• Else if R is invariant in aiand Si= Ti, thenσC ,R(ai)= Si
• ElseσC ,Ris undefined.
The information from the constraint sets is sufficient to infer optimal solutions. For example, given the { a, b }-constraint set E , such that {⊥ → Int <:a <:, ⊥<:b <:I nt}⊆ E, the
minimal substitution from the constraint set E with respect to some type T , such that T = {x : a, y : b→ Int}, would be inferred as σE ,T= [a ⇒ ⊥ → Int, b ⇒ Int].
The simple examples that inferred the instantiations for the type variables a or b highlights an important challenge in understanding local type inference: any debugging technique that analyzes the typing decisions of local type inference has to be aware not only of the final instantiations of type variables, but also the specification that determines the minimal sub- stitution, any least upper bound or greatest lower bound approximations of the involved type constraints, and the types of type constraints themselves.