def test_var_more_unification(): checker = Checker() T = checker.fresh_var() U = checker.fresh_var() checker.unify(Tuple(T, Bool), Tuple(Int, U)) assert checker.unifiers.same_set(T, Int) assert checker.unifiers.same_set(U, Bool)
def infer_type(self, checker: check.Checker) -> typ.Type: pred_type = self.pred.infer_type(checker) checker.unify(pred_type, typ.Bool) yes_type = self.yes.infer_type(checker) no_type = self.no.infer_type(checker) checker.unify(yes_type, no_type) return checker.unifiers.concretize(yes_type)
def infer_type(self, checker: check.Checker) -> typ.Type: # In a new scope, infer the type of the body. # Scoped because `self.param` is valid only inside this scope. # Parameter types are non-generic while checking the body. with checker.new_scope(), checker.scoped_non_generic() as arg_type: checker.type_env[self.param] = arg_type body_type = self.body.infer_type(checker) # After inferring body's type, arg type might be known. arg_type = checker.unifiers.concretize(arg_type) return typ.Fn(arg_type, body_type)
def std_env(checker: check.Checker) -> StdEnv: T = checker.fresh_var() U = checker.fresh_var() V = checker.fresh_var() W = checker.fresh_var() return env.Env(locals={ syntax.Ident("null"): typ.Fn(typ.List(T), typ.Bool), syntax.Ident("tail"): typ.Fn(typ.List(U), typ.List(U)), syntax.Ident("zero"): typ.Fn(typ.Int, typ.Bool), syntax.Ident("succ"): typ.Fn(typ.Int, typ.Int), syntax.Ident("pred"): typ.Fn(typ.Int, typ.Int), syntax.Ident("times"): typ.Fn(typ.Int, typ.Fn(typ.Int, typ.Int)), syntax.Ident("pair"): typ.Fn(V, typ.Fn(W, typ.Tuple(V, W))), })
def test_unification_error(): checker = Checker() T = checker.fresh_var() with pytest.raises(UnificationError): checker.unify(Tuple(Bool, Int), Tuple(T, T)) with pytest.raises(UnificationError): checker.unify(Tuple(Bool, Int), Tuple(Bool)) with pytest.raises(UnificationError): checker.unify(Tuple(Bool, Int), Fn(Bool, Int))
def infer_type(self, checker: check.Checker) -> typ.Type: # Get best guess as to the type of `self.arg`. arg_type = self.arg.infer_type(checker) # Set up a function type. beta = checker.fresh_var() fn_type_joiner = typ.Fn(arg_type, beta) # Ensure the `self.fn` refers to a Fn type. fn_type = self.fn.infer_type(checker) checker.unify(fn_type, fn_type_joiner) # In case beta's root was changed in the last unification, get it's # current root. return checker.unifiers.concretize(beta)
def test_concretize(): checker = Checker() T = checker.fresh_var() U = checker.fresh_var() tup = Tuple(T, Fn(U, Int)) checker.unify(T, List(Bool)) checker.unify(U, T) concrete = checker.concretize(tup) assert concrete == Tuple(List(Bool), Fn(List(Bool), Int))
def infer_type(self, checker: check.Checker) -> typ.Type: # Scope the `left = right` binding. with checker.new_scope(): # First, bind `left` to a fresh type variable. This allows # for recursive let statements. # Note: `alpha` is only non-generic while inferring `right`. TODO: Why tho? with checker.scoped_non_generic() as alpha: checker.type_env[self.left] = alpha # Next infer the type of `right` using the binding just created. right_type = self.right.infer_type(checker) # Link the type variable with the inferred type of `right`. checker.unify(alpha, right_type) # With the environment set up, now the body can be typechecked. return self.body.infer_type(checker)
def test_basic_generic_non_generic_unification_reversed(): checker = Checker() generic = checker.fresh_var() non_generic = checker.fresh_var(non_generic=True) checker.unify(non_generic, generic) assert checker.is_non_generic(generic)
def test_complex_generic_non_generic_unification(): checker = Checker() generic = checker.fresh_var() non_generic = checker.fresh_var(non_generic=True) t = Tuple(generic) checker.unify(non_generic, t) assert checker.is_non_generic(generic)
def test_var_unification(): checker = Checker() T = checker.fresh_var() U = checker.fresh_var() assert not checker.unifiers.same_set(T, U) checker.unify(T, U) assert checker.unifiers.same_set(T, U) checker.unify(T, Bool) assert checker.unifiers.same_set(T, Bool) assert checker.unifiers.same_set(U, Bool)
def test_concrete_atom_unification(): checker = Checker() checker.unify(Int, Int)
def test_concrete_poly_unification(): checker = Checker() checker.unify(Tuple(Int, Bool), Tuple(Int, Bool))
def infer_type(self, checker: check.Checker) -> typ.Type: return checker.duplicate_type(checker.type_env[self])
@pg.production("expr : BOOL_LIT") def bool_lit_expr(s): value = {"true": True, "false": False}[s[0].value] return syntax.Const(value, typ.Bool) @pg.production("expr : IDENT") def ident_expr(s): return syntax.Ident(s[0].value) @pg.production("expr : LPAREN expr RPAREN") def paren_expr(s): return s[1] parser = pg.build() if __name__ == '__main__': def parse(txt): return parser.parse(lexer.lexer.lex(txt)) from hindley_milner.src.check import Checker checker = Checker() code = "let val f = fn a => a in pair (f true) (f 13) end" ast = parse(code) print(ast) ast.infer_type(checker)