def test_pairs(self): # Check the types of the functions involving pairs: # # mk_pair :: a -> b -> (a, b) # fst :: (a, b) -> a # snd :: (a, b) -> b self.check_single_expr( parse('mk_pair'), ('->', 0, ('->', 1, ('Pair', 0, 1)))) self.check_single_expr( parse('fst'), ('->', ('Pair', 0, 1), 0)) self.check_single_expr( parse('snd'), ('->', ('Pair', 0, 1), 1))
def test_let_partial_generalization_with_utils(self): # More thorough test of "let" partial specialization using # utility functions. expr = parse(r""" \x . \y . \z . let const_x = \w . x in mk_pair (mk_pair (unify const_x (mk_fn y True)) (unify const_x (mk_fn (mk_pair True False) z))) const_x """) # In the expression above, if "x" has type "a", "y" has type # "b", and "z" has type "c", then "const_x" is initially # assigned type "forall d . d -> a". On its first use it is # unified with "b -> Bool", forcing "a" to be "Bool". On its # second use, it is unified with "(Bool, Bool) -> c", forcing # "c" to be "Bool". Note that since "d" is qualified with # "forall", "b" is not unified with "(Bool, Bool)". Therefore # the type of the final expression should be: # # Bool -> b -> Bool -> ((b -> Bool, (Bool, Bool) -> Bool), d -> Bool) self.check_single_expr( self.def_utils(expr), ('->', ('Bool',), ('->', 0, ('->', ('Bool',), ('Pair', ('Pair', ('->', 0, ('Bool',)), ('->', ('Pair', ('Bool',), ('Bool',)), ('Bool',))), ('->', 1, ('Bool',)))))))
def test_infinite_type_right(self): # Check that producing an infinite type by unifying "a -> b" # with "a" produces an error. We do so by attempting to # type-check the expression: # # (\f . unify (\x . f) f) self.check_type_error( self.def_utils( parse(r'\f . unify (\x . f) f')))
def def_utils(self, subexpr): # Some useful utility functions for testing complex # unifications are: # # let ignore = \x . True # # "ignore" has type "a -> Bool"; it ignores its argument and # returns a boolean. # # let ignore2 = \x . \y . True # # "ignore2" has type "a -> b -> Bool"; it ignores two # arguments and returns a boolean. # # let second = \x . \y . y # # "second" has type "a -> b -> b"; it ignores its first # argument and returns its second. # # let unify = \x . \y . second (\z . ignore2 (z x) (z y)) x # # "unify" has type "a -> a -> a"; it forces its two arguments # to have the same type, and returns the first argument. # # let mk_fn = \x . \y . \z . second (unify x z) y # # "mk_fn" has type "a -> b -> (a -> b)"; given arguments of # types "a" and "b", it returns a function of type "a -> b". # # This function wraps the given subexpressions in the # necessary "let" constructs so that it can refer to "ignore", # "ignore2", and "unify". fns = [ ('ignore', parse(r'\x . True')), ('ignore2', parse(r'\x . \y . True')), ('second', parse(r'\x . \y . y')), ('unify', parse(r'\x . \y . second (\z . ignore2 (z x) (z y)) x')), ('mk_fn', parse(r'\x . \y . \z . second (unify x z) y')) ] for name, defn in reversed(fns): subexpr = LetExpression(name, defn, subexpr) return subexpr
def test_let_generalization(self): # Check that variables bound with "let" can be used in a # general fashion. For example, in: # # let id = \x . x in (id (\x . x)) (id True) # # The first usage of "id" has type "(Bool -> Bool) -> (Bool -> # Bool)", and the second usage has type "Bool -> Bool", giving # the final expression type "Bool". self.check_single_expr( parse(r'let id = \x . x in (id (\x . x)) (id True)'), ('Bool',))
def test_lambda_non_generalization(self): # In contrast to variables bound by "let" expressions, # variables bound by lambda abstractions are not general. For # example, this should fail to type check: # # (\f . (f (\x . x)) (f True)) # # Because the first usage of f must have type "(a -> a) -> b", # whereas the second usage must have type "Bool -> c", and # these can't be unified. self.check_type_error( parse(r'\f . (f (\x . x)) (f True)'))
def test_infinite_type(self): # Applying a function to itself produces an infinite type. # For example, in the expression: # # (\f . f f) # # if "f" has type "a", then in order to apply "f" to itself, # "a" must be unified with "a -> b". It's impossible to do # this without creating an infinitely recursive type, which we # don't permit. self.check_type_error( parse(r'\f . f f'))
def test_let_shadowing(self): # When a let expression redefines a variable bound in an outer # expression, the outer definition needs to be restored once # the let expression is exited. For example, in: # # (\x . (let x = \y . y in x) x) # # The "x" appearing inside "let x = \y . y in x" has type # "forall b . b -> b", whereas the "x" appearing at the end # has type "a", giving the entire expression type "a -> a". self.check_single_expr( parse(r'\x . (let x = \y . y in x) x'), ('->', 0, 0))
def test_s_combinator(self): # Check the type of the "S" combinator: # # (\x . \y . \z . x z (y z)) # # It sould have type: # # (a -> b -> c) -> (a -> b) -> a -> c self.check_single_expr( parse(r'\x . \y . \z . x z (y z)'), ('->', ('->', 0, ('->', 1, 2)), ('->', ('->', 0, 1), ('->', 0, 2))))
def test_lambda_shadowing(self): # When a lambda expression redefines a variable bound in an # outer expression, the outer definition needs to be restored # once the lambda expression is exited. For example, in: # # (\x . (\x . x True) (\y . x)) # # The "x" appearing inside "(\x . x True)" has type "Bool -> # a", whereas the "x" appearing inside "(\y . x)" has type # "a", giving the entire expression type "a -> a". self.check_single_expr( parse(r'\x . (\x . x True) (\y . x)'), ('->', 0, 0))
def test_nested_lets(self): # When a let-expression appears inside a second # let-expression, type variables appearing in the outer "let" # are not re-generalized. For example, in: # # (\x . let f = (\y . x) in let g = f True in g) # # if "x" has type "a", then "f" is assigned a type of "forall # b . b -> a", and "g" is assigned a type of "a", *not* # "forall a . a". Therefore the whole expression has type (a # -> a). self.check_single_expr( parse(r'\x . let f = \y . x in let g = f True in g'), ('->', 0, 0))
def test_mutually_recursive_type(self): # A more complex example of an infinite type, involving the # mutual recursion of two types, is the expression: # # (\f . \g . ignore2 (unify f (\x . g)) (unify g (\x . f))) # # (where "unify" and "ignore2" are defined in def_utils()). # # If "f" has type "a" and "g" has type "b", this expression # forces "a" to be unified with "c -> b" and forces "b" to be # unified with "d -> a", resulting in a mutual recursion of # two types; which produces an infinite type. self.check_type_error( self.def_utils( parse(r'\f . \g . ignore2 (unify f (\x . g)) (unify g (\x . f))')))
def test_partial_specialization(self): # When a type specialization does not affect the entire type, # we try to short-cut it to avoid creating extraneous type # variables. For example, in: # # (\f . let g = (\x . \y . f y) in g True) # # type-checking of (\x . \y . f y) causes f's type to be # refined to "a -> b", and "g" is assigned a type of "forall c # . c -> a -> b". When "g" is applied to "True", the "a -> b" # portion of the type doesn't need to be specialized. The # final type of the whole expression should be "(a -> b) -> (a # -> b)". self.check_single_expr( parse(r'\f . let g = \x . \y . f y in g True'), ('->', ('->', 0, 1), ('->', 0, 1)))
def test_let_partial_generalization_general_part(self): # In some situations, "let" may generalize some of its type # variables but not others. For example, in: # # (\x . let const_x = (\y . x) in const_x (\z . const_x True)) # # if the type of "x" is "a", then the type assigned to # "const_x" by the "let" expression is "forall b . b -> a". # Then, the first usage of "const_x" is specialized to type (c # -> a) -> a, whereas the second is specialized to "Bool -> # a", giving the final expression type "a -> a". This would # not work if the type assigned to "const_x" did not include # "forall b". self.check_single_expr( parse(r'\x . let const_x = \y . x in const_x (\z . const_x True)'), ('->', 0, 0))
def test_let_partial_generalization_non_general_part(self): # In some situations, "let" may generalize some of its type # variables but not others. For example, in: # # (\x . let const_x = (\y . x) in const_x ((const_x (\z . z)) True)) # # if the type of "x" is initially "a", then the type assigned # to "const_x" by the "let" expression will initially be # "forall b . b -> a". Then, the first usage of "const_x" # will be specialized to "c -> a", and the second will be # specialized to "d -> (Bool -> e)". Since there is no # "forall a" in the type assigned to "const_x", "a" will be # unified with "Bool -> e", so the resulting expression will # have type "(Bool -> e) -> (Bool -> e)". If a "forall a" had # been present, then unification would not have occurred, and # the type would have been "a -> a". self.check_single_expr( parse(r'\x . let const_x = \y . x in const_x ((const_x (\z . z)) True)'), ('->', ('->', ('Bool',), 0), ('->', ('Bool',), 0)))
def test_let_non_generalization(self): # When a variable bound with "let" is generalized, types that # are constrained by declarations in enclosing scopes should # not be generalized. For example, in: # # (\f . let x = f True in f x) # # "let x = f True" constrains f to have type "Bool -> a", # giving "x" a type of "a". Then, "in f x" constrains "x" to # have type Bool. Therefore, "f"'s type becomes "Bool -> # Bool", and the final expression should have type "((Bool -> # Bool) -> Bool)". # # If, at the time of the let-binding, we had over-generalized # the type of "x" to "forall a. a", then when "x" was used in # "in f x", it would have been specialized to a new type # variable "b", which would have been constrained to have type # "Bool", but "a" would have remained general. Therefore, # "f"'s type would have been "Bool -> a", and the final # expressino would have had type "((Bool -> a) -> a)", which # is incorrect. self.check_single_expr( parse(r'\f . let x = f True in f x'), ('->', ('->', ('Bool',), ('Bool',)), ('Bool',)))
def test_let_with_bool(self): self.check_single_expr( parse('let t = True in t'), ('Bool',))
def test_mk_fn_func(self): # Check the type of the "mk_fn" function defined in # def_utils(). self.check_single_expr( self.def_utils(parse('mk_fn')), ('->', 0, ('->', 1, ('->', 0, 1))))
def test_simple_let(self): self.check_single_expr( parse(r'let const = \x . \y . x in const True'), ('->', 0, ('Bool',)))
print """Usage: python main.py <expression> Expression grammar: expression: '(' expression ')' | '\\' identifier '.' expression | 'let' identifier '=' expression 'in' expression | expression expression | identifier Predefined symbols:""" for name in sorted(type_inferrer.get_builtin_names()): expr = lambda_calculus.Variable(name) ty = type_inferrer.visit(expr) canonical_ty = type_inferrer.canonicalize(ty, {}) print ' {0} :: {1}'.format( name, algorithm_w.pretty_print_canonical_type(canonical_ty)) exit(1) expr = lambda_calculus.parse(sys.argv[1]) ty = type_inferrer.visit(expr) canonical_ty = type_inferrer.canonicalize(ty, {}) print """Expression: {0} has type: {1} """.format(expr, algorithm_w.pretty_print_canonical_type(canonical_ty))
def test_polymorphic_binding_example(self): self.check_single_expr( parse(r'let id = \x . x in mk_pair (id True) (id id)'), ('Pair', ('Bool',), ('->', 0, 0)))
def test_isJust(self): self.check_single_expr( parse(r'maybe False (\x . True)'), ('->', ('Maybe', 0), ('Bool',)))
def test_curry(self): # Check the type of the curry function self.check_single_expr( parse(r'\f . \x . \y . f (mk_pair x y)'), ('->', ('->', ('Pair', 0, 1), 2), ('->', 0, ('->', 1, 2))))
def test_uncurry(self): # Check the type of the uncurry function self.check_single_expr( parse(r'\f . \p . f (fst p) (snd p)'), ('->', ('->', 0, ('->', 1, 2)), ('->', ('Pair', 0, 1), 2)))
#!/usr/bin/python3 """Archivo principal de lambda.""" from lambda_calculus import parse, lex from sys import argv if len(argv) > 1: if argv[1] == '--debug': string = argv[2] # print(lex(string)) p = parse(string) print('Fin.') print(p) print(p.value(), ' : ', p.type()) else: p = parse(argv[1]) if p and p.value(): print(p.value(), ' : ', p.type())
def test_already_unified(self): # Make sure that nothing goes wrong when unifying two types # that are already the same. self.check_single_expr( self.def_utils(parse(r"\x . unify x x")), ('->', 0, 0))
def test_unify_func(self): # Check the type of the "unify" function defined in # def_utils(). self.check_single_expr( self.def_utils(parse('unify')), ('->', 0, ('->', 0, 0)))
def test_boolean_example(self): self.check_single_expr( parse(r'\b . if b False True'), ('->', ('Bool',), ('Bool',)))
def test_ignore2_func(self): # Check the type of the "ignore2" function defined in # def_utils(). self.check_single_expr( self.def_utils(parse('ignore2')), ('->', 0, ('->', 1, ('Bool',))))
def test_monomorphic_binding_example(self): self.check_type_error( parse(r'(\id . mk_pair (id True) (id id)) (\x . x)'))
def test_second_func(self): # Check the type of the "second" function defined in # def_utils(). self.check_single_expr( self.def_utils(parse('second')), ('->', 0, ('->', 1, 1)))
"""Archivo principal de Lambda Calculus parser.""" from lambda_calculus import parse from lambda_calculus import reduce_expression import sys inputs_file = sys.argv[1] with open(inputs_file, 'r') as inputs: for input_line in inputs: input_entry, result = input_line.split(',') print "Input value: " print "----> " + input_entry output = reduce_expression(parse(input_entry)) print "Output value: " if isinstance(output, str): print output else: print "----> " + str(output) + ":" + output.arity() print "Expected value: " print "----> " + result #output, arity = reduce_expression(parse(input_entry)) #print "Output value: " #if isinstance(output, str): # print output #else: # print "----> " + str(output) + ":" + arity #print "Expected value: " #print "----> " + result
# Nuestros tests. ('(\\z:Nat. pred(z)) ((\\x:Nat->Nat. x succ(succ(0))) \\y:Nat. y)', '1', 'Nat'), ('(\\x:Nat.succ(x)) 0 0', None, None), ('(\\x:Nat->Nat. x succ(succ(0))) \y:Nat. y', '2', 'Nat'), ('(\\x:Nat->Nat. x pred(succ(0))) \y:Nat. y', '0', 'Nat'), ('(\\x:Nat. if iszero(x) then succ(x) else pred(x)) pred(0)', '1', 'Nat'), ('(\\x:Nat. if iszero(x) then succ(x) else pred(x)) succ(0)', '0', 'Nat'), ('(\\x:Nat. if iszero(x) then succ(x) then pred(x)) succ(0)', None, None), ('(\\x:Nat. if iszero(x) then succ(x) then true) succ(0)', None, None), ] for test in tests: try: p = parse(test[0]) if str(p.value()) == test[1] and str(p.type()) == test[2]: print('PASSED', test[0]) else: print('FAILED', test[0]) if str(p.value()) != test[1]: print('Got', str(p.value()), 'but expected', test[1]) if str(p.type()) != test[2]: print('Got', str(p.type()), 'but expected', test[2]) except: if test[1] is None: print('PASSED', test[0]) else: print('FAILED', test[0])