Example #1
0
def typeof(obj):
    """Returns the type of an object within the internal type system.

    :param obj: the object to inspect.

    :returns: An object representing the type in the internal type system
              language (i.e., a
              `~hask3.lang.hindley_milner.TypeOperator`:class: or
              `~hask3.lang.hindley_milner.TypeVariable`:class:).

    """
    from hask3.hack import is_python_function
    from hask3.lang.hindley_milner import TypeVariable, TypeOperator, Tuple
    # XXX: WTF?
    TypeVariable.next_var_name = 'a'
    if isinstance(obj, Hask):
        return obj.__type__()
    elif isinstance(obj, tuple):
        return Tuple([typeof(o) for o in obj])
    elif obj is None:
        return TypeOperator(None, [])
    elif is_python_function(obj):
        return TypeOperator(PyFunc, [])
    else:
        return TypeOperator(type(obj), [])
Example #2
0
def build_sig_arg(arg, cons, var_dict):
    """Covert a type signature argument into its internal representation.

    :param arg: The argument (a string, a Python type, etc) to convert.

    :param cons: A dictionary of typeclass constraints for the type
           signature.

     :param var_dict: a dictionary of bound type variables.

    :returns: A TypeVariable or TypeOperator representing the `arg`.

    :raises TypeSignatureError: if the argument cannot be converted.

    """
    from hask3.lang.hindley_milner import TypeVariable, TypeOperator
    from hask3.lang.hindley_milner import Tuple, ListType
    if isinstance(arg, str):
        if arg.islower():
            if arg not in var_dict:
                kw = {'constraints': cons[arg]} if arg in cons else {}
                var_dict[arg] = TypeVariable(**kw)
            res = var_dict[arg]
        else:
            res = None
    # sub-signature, e.g. H/ (H/ int >> int) >> int >> int
    elif isinstance(arg, TypeSignature):
        res = make_fn_type(build_sig(arg, var_dict))
    # HKT, e.g. t(Maybe "a") or t("m", "a", "b")
    elif isinstance(arg, TypeSignatureHKT):
        if type(arg.tcon) == str:
            hkt = build_sig_arg(arg.tcon, cons, var_dict)
        else:
            hkt = arg.tcon
        types = [build_sig_arg(a, cons, var_dict) for a in arg.params]
        res = TypeOperator(hkt, types)
    # None (the unit type)
    elif arg is None:
        res = TypeOperator(None, [])
    # Tuples: ("a", "b"), (int, ("a", float)), etc.
    elif isinstance(arg, tuple):
        f = lambda x: build_sig_arg(x, cons, var_dict)
        res = Tuple([f(x) for x in arg])
    # Lists: ["a"], [int], etc.
    elif isinstance(arg, list):
        if len(arg) == 1:
            res = ListType(build_sig_arg(arg[0], cons, var_dict))
        else:
            res = None
    # any other type, builtin or user-defined
    elif isinstance(arg, type):
        res = TypeOperator(arg, [])
    else:
        res = None
    if res is not None:
        return res
    else:
        raise TypeSignatureError(f"Invalid item in type signature: {arg}")
Example #3
0
 def __type__(self):
     args = [
         typeof(self[fields.index(p)])
         if p in fields else TypeVariable()
         for p in type_constructor.__params__
     ]
     return TypeOperator(type_constructor, args)
Example #4
0
    def setUp(self):
        """Create some basic types and polymorphic typevars, a toy environment,
           and some AST nodes
        """
        self.var1 = TypeVariable()
        self.var2 = TypeVariable()
        self.var3 = TypeVariable()
        self.var4 = TypeVariable()
        self.Pair = TypeOperator("*", (self.var1, self.var2))
        self.Bool = TypeOperator("bool", [])
        self.Integer = TypeOperator("int", [])
        self.NoneT = TypeOperator("None", [])

        # toy environment
        self.env = {
            "pair":
            Function(self.var1, Function(self.var2, self.Pair)),
            "True":
            self.Bool,
            "None":
            self.NoneT,
            "id":
            Function(self.var4, self.var4),
            "cond":
            Function(self.Bool,
                     Function(self.var3, Function(self.var3, self.var3))),
            "zero":
            Function(self.Integer, self.Bool),
            "pred":
            Function(self.Integer, self.Integer),
            "times":
            Function(self.Integer, Function(self.Integer, self.Integer)),
            "4":
            self.Integer,
            "1":
            self.Integer
        }

        # some expressions to play around with
        self.compose = Lam(
            "f", Lam("g", Lam("arg", App(Var("g"), App(Var("f"),
                                                       Var("arg"))))))
        self.pair = App(App(Var("pair"), App(Var("f"), Var("1"))),
                        App(Var("f"), Var("True")))
Example #5
0
def make_type_const(name, typeargs):
    """Build a new type constructor given a name and the type parameters.

    :param name: the name of the new type constructor to be created.

    :param typeargs: the type parameters to the constructor.

    :returns: A new class that acts as a type constructor.

    """
    from hask3.lang.hindley_milner import TypeVariable, TypeOperator

    def raise_fn(err):
        raise err

    default_attrs = {"__params__": tuple(typeargs), "__constructors__": ()}
    cls = type(name, (ADT, ), default_attrs)

    cls.__type__ = lambda self: \
        TypeOperator(cls, [TypeVariable() for i in cls.__params__])

    # Unless typeclass instances are provided or derived, ADTs do not support
    # any of these attributes, and trying to use one is a TypeError
    magic = {
        'iter', 'contains', 'add', 'radd', 'rmul', 'mul', 'lt', 'gt', 'le',
        'ge', 'eq', 'ne'
    }
    magic = {f'__{op}__' for op in magic} | {'count', 'index'}
    for attr in magic:
        setattr(cls, attr, lambda self: raise_fn(TypeError))

    # Unless Show is instantiated/derived, use object's `repr` method
    cls.__repr__ = object.__repr__
    cls.__str__ = object.__str__

    return cls
Example #6
0
    def test_signature_build(self):
        """Make sure type signatures are built correctly"""
        # int -> int
        self.unified(make_fn_type(build_sig((H / int >> int).sig)),
                     Function(TypeOperator(int, []), TypeOperator(int, [])))

        # a -> a
        a = TypeVariable()
        self.unified(make_fn_type(build_sig((H / "a" >> "a").sig)),
                     Function(a, a))

        # a -> b
        a, b = TypeVariable(), TypeVariable()
        self.unified(make_fn_type(build_sig((H / "a" >> "b").sig)),
                     Function(a, b))

        # (int -> int) -> int -> int
        self.unified(
            make_fn_type(build_sig((H / (H / int >> int) >> int >> int).sig)),
            Function(Function(TypeOperator(int, []), TypeOperator(int, [])),
                     Function(TypeOperator(int, []), TypeOperator(int, []))))
Example #7
0
    def test_typecheck_builtins(self):
        """Make sure builtin types typecheck correctly"""

        # 1 :: int
        self.unified(typeof(1), TypeOperator(int, []))

        # "a" :: str
        self.unified(typeof("a"), TypeOperator(str, []))

        # Nothing :: Maybe a
        self.unified(typeof(Nothing), TypeOperator(Maybe, [TypeVariable()]))

        # Just(1) :: Maybe int
        self.unified(typeof(Just(1)),
                     TypeOperator(Maybe, [TypeOperator(int, [])]))

        # Just(Just(Nothing)) :: Maybe (Maybe (Maybe a))
        self.unified(
            typeof(Just(Just(Nothing))),
            TypeOperator(
                Maybe,
                [TypeOperator(Maybe, [TypeOperator(Maybe, [TypeVariable()])])
                 ]))

        # Right("error") :: Either a str
        self.unified(
            typeof(Right("error")),
            TypeOperator(
                Either, [TypeVariable(), TypeOperator(str, [])]))

        # Left(2.0) :: Either float a
        self.unified(
            typeof(Left(2.0)),
            TypeOperator(Either, [TypeOperator(float, []),
                                  TypeVariable()]))
Example #8
0
    def test_build_sig_item(self):
        """Test type signature building internals - make sure that types are
           translated in a reasonable way"""
        class example:
            pass

        # type variables
        self.assertTrue(isinstance(build_sig_arg("a", {}, {}), TypeVariable))
        self.assertTrue(isinstance(build_sig_arg("abc", {}, {}), TypeVariable))

        # builtin/non-ADT types
        self.unified(build_sig_arg(str, {}, {}), TypeOperator(str, []))
        self.unified(build_sig_arg(int, {}, {}), TypeOperator(int, []))
        self.unified(build_sig_arg(float, {}, {}), TypeOperator(float, []))
        self.unified(build_sig_arg(list, {}, {}), TypeOperator(list, []))
        self.unified(build_sig_arg(set, {}, {}), TypeOperator(set, []))
        self.unified(build_sig_arg(example, {}, {}), TypeOperator(example, []))

        # unit type (None)
        self.unified(build_sig_arg(None, {}, {}), TypeOperator(None, []))

        # tuple
        self.unified(build_sig_arg((int, int), {}, {}),
                     Tuple([TypeOperator(int, []),
                            TypeOperator(int, [])]))
        self.unified(
            build_sig_arg((None, (None, int)), {}, {}),
            Tuple([
                TypeOperator(None, []),
                Tuple([TypeOperator(None, []),
                       TypeOperator(int, [])])
            ]))
        a = TypeVariable()
        self.unified(build_sig_arg(("a", "a", "a"), {}, {}), Tuple([a, a, a]))

        # list
        self.unified(typeof(L[[]]), build_sig_arg(["a"], {}, {}))
        self.unified(typeof(L[1, 1]), build_sig_arg([int], {}, {}))
        self.unified(typeof(L[[L[1, 1]]]), build_sig_arg([[int]], {}, {}))

        # adts
        self.unified(typeof(Nothing), build_sig_arg(t(Maybe, "a"), {}, {}))
        self.unified(typeof(Just(1)), build_sig_arg(t(Maybe, int), {}, {}))
        self.unified(typeof(Just(Just(Nothing))),
                     build_sig_arg(t(Maybe, t(Maybe, t(Maybe, "a"))), {}, {}))
        self.unified(typeof(Right("error")),
                     build_sig_arg(t(Either, str, "a"), {}, {}))
        self.unified(typeof(Left(2.0)),
                     build_sig_arg(t(Either, "a", int), {}, {}))
        self.unified(typeof(Just(__ + 1)),
                     build_sig_arg(t(Maybe, "a"), {}, {}))
        self.unified(typeof(Just(__ + 1)),
                     build_sig_arg(t(Maybe, (H / "a" >> "b")), {}, {}))
Example #9
0
    def test_type_checking(self):
        """Basic type checking in our toy environment"""

        # 1 :: Integer
        self.typecheck(Var("1"), self.Integer)

        # 1 :: Bool ==> TypeError
        self.not_typecheck(Var("1"), self.Bool)

        # (\x -> x) :: (a -> a)
        v = TypeVariable()
        self.typecheck(Lam("n", Var("n")), Function(v, v))

        # type(id) == type(\x -> x)
        self.typecheck(Lam("n", Var("n")), self.env["id"])

        # (\x -> x) :: (a -> b)
        self.typecheck(Lam("n", Var("n")),
                       Function(TypeVariable(), TypeVariable()))

        # (id 1) :: Integer
        self.typecheck(App(Var("id"), Var("1")), self.Integer)

        # (id 1) :: Bool ==> TypeError
        self.not_typecheck(App(Var("id"), Var("1")), self.Bool)

        # pred :: (Integer -> Integer)
        self.typecheck(Var("pred"), Function(self.Integer, self.Integer))

        # (pred 4) :: Integer
        self.typecheck(App(Var("pred"), Var("1")), self.Integer)

        # ((pair 1) 4) :: (a, b)
        self.typecheck(
            App(App(Var("pair"), Var("1")), Var("4")),
            TypeOperator("*", [TypeVariable(), TypeVariable()]))

        # (*) :: (Integer -> Integer -> Integer)
        self.typecheck(
            Var("times"),
            Function(self.Integer, Function(self.Integer, self.Integer)))

        # (* 4) :: (Integer -> Integer)
        self.typecheck(App(Var("times"), Var("4")),
                       Function(self.Integer, self.Integer))

        # (* 4) :: (Bool -> Integer) ==> TypeError
        self.not_typecheck(App(Var("times"), Var("4")),
                           Function(self.Bool, self.Integer))

        # (* 4) :: (Integer -> a) ==> TypeError
        self.not_typecheck(App(Var("times"), Var("4")),
                           Function(self.Integer, TypeVariable))

        # ((* 1) 4) :: Integer
        self.typecheck(App(App(Var("times"), Var("1")), Var("4")),
                       self.Integer)

        # ((* 1) 4) :: Bool ==> TypeError
        self.not_typecheck(App(App(Var("times"), Var("1")), Var("4")),
                           self.Bool)

        # let g = (\f -> 5) in (g g) :: Integer
        self.typecheck(Let("g", Lam("f", Var("4")), App(Var("g"), Var("g"))),
                       self.Integer)

        # (.) :: (a -> b) -> (b -> c) -> (a -> c)
        a, b, c = TypeVariable(), TypeVariable(), TypeVariable()
        self.typecheck(
            self.compose,
            Function(Function(a, b), Function(Function(b, c), Function(a, c))))

        # composing `id` with `id` == `id`
        # ((. id) id) :: (a -> a)
        d = TypeVariable()
        self.typecheck(App(App(self.compose, Var("id")), Var("id")),
                       Function(d, d))

        # composing `id` with `times 4`
        # ((. id) (* 2)) :: (int -> int)
        self.typecheck(
            App(App(self.compose, Var("id")), App(Var("times"), Var("4"))),
            Function(self.Integer, self.Integer))

        # composing `times 4` with `id`
        # ((. (* 2)) id) :: (int -> int)
        self.typecheck(
            App(App(self.compose, App(Var("times"), Var("4"))), Var("id")),
            Function(self.Integer, self.Integer))

        # basic closure
        # ((\x -> (\y -> ((* x) y))) 1) :: (Integer -> Integer)
        self.typecheck(
            App(Lam("x", Lam("y", App(App(Var("times"), Var("x")), Var("y")))),
                Var("1")), Function(self.Integer, self.Integer))

        # lambdas have lexical scope
        # (((\x -> (\x -> x)) True) None) :: NoneT
        self.typecheck(
            App(App(Lam("x", Lam("x", Var("x"))), Var("True")), Var("None")),
            self.NoneT)

        # basic let expression
        # let a = times in ((a 1) 4) :: Integer
        self.typecheck(
            Let("a", Var("times"), App(App(Var("a"), Var("1")), Var("4"))),
            self.Integer)

        # let has lexical scope
        # let a = 1 in (let a = None in a) :: NoneT
        self.typecheck(Let("a", Var("1"), Let("a", Var("None"), Var("a"))),
                       self.NoneT)

        # let polymorphism
        # let f = (\x -> x) in ((f 4), (f True)) :: (Integer, Bool)
        self.typecheck(Let("f", Lam("x", Var("x")), self.pair),
                       TypeOperator("*", [self.Integer, self.Bool]))

        # recursive let
        # (factorial 4) :: Integer
        self.typecheck(
            Let(
                "factorial",  # letrec factorial =
                Lam(
                    "n",  # fn n =>
                    App(
                        App(  # cond (zero n) 1
                            App(
                                Var("cond"),  # cond (zero n)
                                App(Var("zero"), Var("n"))),
                            Var("1")),
                        App(  # times n
                            App(Var("times"), Var("n")),
                            App(Var("factorial"),
                                App(Var("pred"), Var("n")))))),  # in  # noqa
                App(Var("factorial"), Var("4"))),
            self.Integer)