def unify_constraints(constraints, solution=None): """ Blaze type unification. Two types unify if: - They have the same type constructor, and - They have an equal number of sub-terms which unify element-wise Our algorithm is different from a conventional implementation in that we have a different notion of equality since we allow coercion which we solve by generating new coercion constraints in the form of [(A coerces to B)], where A and B are type variables. Parameters ---------- constraints : [(Mono, Mono)] List of constraints (blaze type equations) Returns: { TypeVar : set([ Mono ]) } Returns a solution to the set of constraints. The solution is a set of bindings (a substitution) from type variables to type sets. """ solution = IdentityDict(solution) remaining = [] # Initialize solution for t1, t2 in constraints: for freevar in chain(free(t1), free(t2)): if freevar not in solution: solution[freevar] = set() # Calculate solution for t1, t2 in constraints: unify_single(t1, t2, solution, remaining) return solution, remaining
def unify_single(t1, t2, solution, remaining): """ Unify a single type equation and update the solution and remaining constraints. """ if isinstance(t1, TypeVar) and isinstance(t2, TypeVar): remaining.append((t1, t2)) elif isinstance(t1, TypeVar): if t1 in free(t2): raise error.UnificationError("Cannot unify recursive types") solution[t1].add(t2) remaining.append((t1, t2)) elif isinstance(t2, TypeVar): if t2 in free(t1): raise error.UnificationError("Cannot unify recursive types") solution[t2].add(t1) elif isinstance(t1, TypeConstructor): verify(t1, t2) elif not isinstance(t1, Mono) and not isinstance(t2, Mono): verify(t1, t2) elif getattr(t1, "cls", None) == MEASURE and getattr(t2, "cls", None) == MEASURE: # If both types are measures, verify they can be promoted promote_units(t1, t2) else: verify(t1, t2) for arg1, arg2 in zip(t1.parameters, t2.parameters): unify_single(arg1, arg2, solution, remaining)
def unify_single(t1, t2, solution, remaining): """ Unify a single type equation and update the solution and remaining constraints. """ if isinstance(t1, TypeVar) and isinstance(t2, TypeVar): remaining.append((t1, t2)) elif isinstance(t1, TypeVar): if t1 in free(t2): raise error.UnificationError("Cannot unify recursive types") solution[t1].add(t2) remaining.append((t1, t2)) elif isinstance(t2, TypeVar): if t2 in free(t1): raise error.UnificationError("Cannot unify recursive types") solution[t2].add(t1) elif isinstance(t1, TypeConstructor): verify(t1, t2) elif not free(t1) and not free(t2): # No need to recurse, this will be caught by promote() pass else: verify(t1, t2) for arg1, arg2 in zip(t1.parameters, t2.parameters): unify_single(arg1, arg2, solution, remaining)
def reify(solution, S=None): """ Reify a typing solution, returning a new solution with types as concrete types as opposed to type sets. Parameters ---------- solution : { TypeVar : set([ Type ]) } Typing solution Returns: { TypeVar : Type } Returns a solution reduced to concrete types only. """ if S is None: S = IdentityDict() seen = set() queue = deque(dict_iteritems(solution)) while queue: typevar, t = queue.popleft() t = frozenset(t) if typevar in S: continue typeset = solution[typevar] freevars = IdentityDict.fromkeys(chain(*[free(t) for t in typeset])) if not typeset: S[typevar] = typevar typeset.add(typevar) continue elif freevars and (typevar, t) not in seen: # Reify dependencies first queue.append((typevar, t)) seen.add((typevar, t)) elif freevars: typeset = set(substitute(S, t) for t in typeset) S[typevar] = promote_units(*typeset) return S