def test_datatype_tree():
    assert parse("data Tree a = Leaf a | Branch (Tree a) (Tree a)") == [
        DataType(
            "Tree",
            Type.from_str("Tree a"),
            [
                DataCons("Leaf", [Type.from_str("a")]),
                DataCons("Branch",
                         [Type.from_str("Tree a"),
                          Type.from_str("Tree a")]),
            ],
        )
    ]
Beispiel #2
0
def test_local_type_annotation_letrec():
    phi, t = typecheck(
        parse_expression("""let count :: Number -> [Number]
                   count x = x:count (x + 1)
                   g :: [a]
                   g = count 1
                   g2 = reverse g
                in g2"""),
        BuiltinEnvDict({
            "reverse": TypeScheme.from_str("[a] -> [a]"),
            ":": TypeScheme.from_str("a -> [a] -> [a]"),
            "+": TypeScheme.from_str("a -> a -> a"),
        }),
    )
    assert t == Type.from_str("[Number]")

    with pytest.raises(TypeError):
        typecheck(
            parse_expression("""let count :: Number -> [Number]
                       count x = x:count (x + 1)
                       g :: [Char]
                       g = count 1
                    in reverse g"""),
            BuiltinEnvDict({
                "reverse": TypeScheme.from_str("[a] -> [a]"),
                ":": TypeScheme.from_str("a -> [a] -> [a]"),
                "+": TypeScheme.from_str("a -> a -> a"),
            }),
        )
Beispiel #3
0
def test_typecheck_recursion():
    then = TypeScheme.from_str("a -> Then a")
    else_ = TypeScheme.from_str("a -> Else a")
    if_then_else = TypeScheme.from_str("Bool -> Then a -> Else a -> a")
    Nil = TypeScheme.from_str("[a]")
    tail = TypeScheme.from_str("[a] -> [a]")
    matches = TypeScheme.from_str("a -> b -> Bool")
    add = TypeScheme.from_str("a -> a -> a")
    env = BuiltinEnvDict({
        "if": if_then_else,
        "then": then,
        "else": else_,
        "matches": matches,
        "Nil": Nil,
        "+": add,
        "tail": tail,
    })
    phi, t = typecheck(
        # I need to put parenthesis because of the failure of precedence we
        # have; otherwise we could use $ to connect if then and else (they are
        # still functions): 'if cond $ then result $ else other_result'.
        # `matches` would be a simple pattern matching function.  The real
        # function would have to operate on values and patterns (which are no
        # representable here.)
        parse_expression("""
            let count xs = if (xs `matches` Nil) \
                              (then 0) \
                              (else let ts = tail xs in 1 + (count ts))
            in count
            """),
        env,
    )
    # The count functions counts the number of elements.
    unify(Type.from_str("[a] -> Number"), t)
def test_datatype_simple2():
    assert (parse("""
            data Then a = Then a
            data Else a = Else a
            """) == [
        DataType(
            "Then",
            Type.from_str("Then a"),
            [DataCons("Then", [Type.from_str("a")])],
        ),
        DataType(
            "Else",
            Type.from_str("Else a"),
            [DataCons("Else", [Type.from_str("a")])],
        ),
    ])
Beispiel #5
0
def test_combinators():
    # Since they're closed expressions they should type-check
    K = parse_expression(r"\a b -> a")
    TK = Type.from_str("a -> b -> a")
    phi, t = typecheck(K, EMPTY_TYPE_ENV)
    # we can't ensure TK == t, but they must unify, in fact they
    # must be same type with alpha-renaming.
    unify(TK, t)

    S = parse_expression(r"\x y z -> x z (y z)")
    TS = Type.from_str("(a -> b -> c) -> (a -> b) -> a -> c")
    phi, t = typecheck(S, EMPTY_TYPE_ENV)
    unify(TS, t)

    # But the paradoxical combinator doesn't type-check
    Y = parse_expression(r"\f -> (\x -> f (x x))(\x -> f (x x))")
    with pytest.raises(TypeError):
        phi, t = typecheck(Y, EMPTY_TYPE_ENV)
Beispiel #6
0
def test_local_type_annotation_let():
    phi, t = typecheck(
        parse_expression("""let g = [1, 2, 3]
               in reverse g"""),
        BuiltinEnvDict({
            "reverse": TypeScheme.from_str("[a] -> [a]"),
            ":": TypeScheme.from_str("a -> [a] -> [a]"),
        }),
    )
    assert t == Type.from_str("[Number]")

    phi, t = typecheck(
        parse_expression("""let g :: [Number]
                   g = []
               in reverse g"""),
        BuiltinEnvDict({
            "reverse": TypeScheme.from_str("[a] -> [a]"),
            ":": TypeScheme.from_str("a -> [a] -> [a]"),
        }),
    )
    assert t == Type.from_str("[Number]")

    phi, t = typecheck(
        parse_expression("""let g :: [a]
                   g = [1, 2, 3]
                in reverse g"""),
        BuiltinEnvDict({
            "reverse": TypeScheme.from_str("[a] -> [a]"),
            ":": TypeScheme.from_str("a -> [a] -> [a]"),
        }),
    )
    assert t == Type.from_str("[Number]")

    with pytest.raises(TypeError):
        typecheck(
            parse_expression("""let g :: [Char]
                       g = [1, 2, 3]
                   in reverse g"""),
            BuiltinEnvDict({
                "reverse": TypeScheme.from_str("[a] -> [a]"),
                ":": TypeScheme.from_str("a -> [a] -> [a]"),
            }),
        )
Beispiel #7
0
def test_composition():
    phi, t = typecheck(parse_expression("let id x = x in id . id"),
                       builtins_env)
    unify(Type.from_str("a -> a"), t)
    unify(Type.from_str("(a -> a) -> (a -> a)"), t)

    phi, t = typecheck(parse_expression("Left . Right"), builtins_env)
    unify(Type.from_str("a -> Either (Either b a) c"), t)

    # In our case, (+) is not polymorphic (Number is not a type-class), so it
    # can't be composed with Either.
    with pytest.raises(TypeError):
        typecheck(parse_expression("(+) . Left"), builtins_env)
    # If we had a polymorphic (+), it would be composable
    phi, t = typecheck(
        parse_expression("(+) . Left"),
        dict(builtins_env, **{"+": TypeScheme.from_str("a -> a -> a")}),
    )
    unify(Type.from_str("a -> Either a b -> Either a b"), t)
    phi, t = typecheck(
        parse_expression("(+) . Right"),
        dict(builtins_env, **{"+": TypeScheme.from_str("a -> a -> a")}),
    )
    unify(Type.from_str("b -> Either a b -> Either a b"), t)
Beispiel #8
0
def test_basic_builtin_types():
    with pytest.raises(TypeError):
        # not :: Bool -> Bool, but passed a Number
        typecheck(parse_expression("not 0"), builtins_env)

    phi, t = typecheck(parse_expression("not True"), builtins_env)
    assert t == BoolType
    phi, t = typecheck(parse_expression("not False"), builtins_env)
    assert t == BoolType

    userfuncs = {"toString": TypeScheme.from_str("a -> [Char]")}
    phi, t = typecheck(parse_expression("either toString id"),
                       dict(builtins_env, **userfuncs))
    assert len(find_tvars(t)) == 1
    unify(Type.from_str("Either a [Char] -> [Char]"), t)
Beispiel #9
0
def test_regression_missing_dynamic_builtins():
    phi, t = typecheck(parse_expression("let pair x y = (x, y) in pair 1 2"))
    assert unify(t, Type.from_str("(Number, Number)"))
Beispiel #10
0
def test_datatype_simple():
    assert parse("data Then a = Then a") == [
        DataType("Then", Type.from_str("Then a"),
                 [DataCons("Then", [Type.from_str("a")])])
    ]