Package: typed.py
typed.pyimplements multimethods (also known as multiple dispatch) for
Python. However, instead of basing the types used for dispatch on the
standard Python class system,typed.pyuses a method calledinquisitive
types.Diving InStart off by importing thetypedmodule; you don’t need anything besides
that.from typed import *Some of the names in the typed module may conflict with names you yourself use:typed,typedef,restrict,fn,from_pytype, andeq. If
so, take special care to eliminate shadowing.Now then, let’s write a function:@typed(String, String)
def combine(a, b):
    return a + bThis function will require both arguments to be strings:>>> combine("adsf", "adsf")
'asdfasdf'
>>> combine(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "typed/specialize.py", line 108, in __call__
    return self.match(args)[1](*args)
File "typed/specialize.py", line 103, in match
    raise TypedError("No matches", self, [x[0] for x in self.sigs])
typed.specialize.TypedError: No matches for function <typed function combine at 0xb733a4ec>. Alternatives:
        (<Type String>, <Type String>)Scary looking error, huh? Note that it tells you the alternative signatures.Anyway, let’s define another variant to this function:@typed(String, Integer)
def combine(a, n):
    return a + str(n)And, we can use it normally:>>> combine("asdf", 12)
'asdf12'
>>> combine("asdf", "asdf")
'asdfasdf'Yay, both signatures work!The builtin types include:Object(anything at all, not just things that inherit fromobject)Complex(complex,float, or and integral type)Real(floator an integral type)Integer(intorlong; an integral type)Boolean(bool)String(guess!)Type(An inquisitive type)PyType(A python class or type)Tuple(A tuple of any length or type)List(A list of any type) andList(T)(A list of typeTelements)Dict(A dict of any type to any type),Dict(T)(A list where all
keys have typeT), andDict(K, V)(A dict with keys of typeKand values of typeV)OneOf(*x)(Type of things equal to one of the elements ofx)What are Inquisitive Types?An inquisitive type is defined by two properties:isandsub. I’ll skip the
second for now, butis(which in Python is the methodis_of a type) just
takes an object and tells you if that object is an instance of the given type.
For example, theismethod forObjectis simply:def is(self, x):
    return TrueBecause, after all, everything is an object. Not thata priori, objects have
no type; one can only ask a type if an object is an instance. This is why they
are calledinquisitive: you always have to ask if you want to know if an object
is an instance of something.Now, what about thatsubmethod I mentioned? This is the analog ofissubclassfor inquisitive types: it takes another type, and tells you if
the given type is a subclass of it. For example, the one forObjectis:def sub(self, X):
    if X == self:
        return True
    else:
        return FalseThese subclass relations are used to determine which method to call if a
function has, for example, two implementations with signaturesInteger, IntegerandReal, Real: clearly you want the one withIntegers to
be called if possible; and it is, becauseInteger.sub(Real)returnsTrue.Now, the nice thing is that this allows you to construct more complex types
easily, usingtype functions. Don’t worry, the concept is easy: it’s a
function that takes a type and returns a type. For example, consider the
functionrestrict.restrict(T, f, name)takes a typeTand returns
a type that only says things are instances if they are instances of that type,andsatisfy the functionf. Thenamejust names the resulting type.
So, for example:>>> EvenInt = restrict(Integer, lambda x: x % 2 == 0, "EvenInt")
<type EvenInt>
>>> isinstance(4, EvenInt)
True
>>> isinstance(3, EvenInt)
FalseSome other type functions are built in totyped.py.fn(f)is another way
of sayingrestrict(Object, f).typedef(T, name)copies the typeTand renames it (sometimes useful if you use type functions to construct types).eq(x)creates a new type whose only instance isx; it’s equivalent toOneOf(x). Finally,from_pytypeis a compatibility feature, taking any
Python type and returning a new inquisitive type.Furthermore, a few useful type functions have nice operator syntax. For example:>>> isinstance((3, 4), Integer * Integer)
True
>>> isinstance(("foo", "bar", "baz"), String ** 3)
True
>>> isinstance(5, Integer < 3)
False
>>> isinstance(7, Integer > -2)
True
>>> isinstance("a", eq("a") | eq("b"))
True
>>> map(lambda x: isinstance(x, Integer | String), [1, "a", 17.3, "17.3", True])
[True, True, False, True, False]
>>> isinstance(12, (3 < Integer) < 17)
TrueNote that in the last example, youmustplace parentheses around one of the
two comparisons. It doesn’t matter which one; this is simply a fault of Python’s
syntax.Use in APIsOne can usetyped.pyfor simple type checking:@typed(Real)
def sqrt(x):
    ...Or, you can use it to dispatch based on type:@typed(Real)
def sqrt(x):
    import math
    return math.sqrt(x)

@typed(Complex)
def sqrt(x):
    import cmath
    return cmath.sqrt(x)You can eliminate boring conditional checks:@typed(Integer < 10000)
def is_prime(x):
    return do_bruteforce(x)

@typed(Integer)
def is_prime(x):
    return elliptic_curve_primality_test(x)You can also dispatch, because these are inquisitive types, on specifics of the
arguments:@typed(restrict(String, lambda x: x.startswith("http")))
def download(x):
    import urllib
    return urllib.urlopen(x).read()

@typed(restrict(String, lambda x: x.startswith("ftp")))
def download(x):
    import ftplib
    f = ftplib.FTP(dir)
    return f.getwelcome()Finally, you can combine these to make those strings you ask people to pass to
identify things to do type-safe:Color = typedef(OneOf("green", "yellow", "red"), "Color")

@typed(Banana, Color)
def eat(b, color):
    if color == "green":
        print "Hold on"
    elif color == "yellow":
        print "Go ahead"
    elif color == "red":
        print "Where the **** did you get that banana?" # -- Mitch HedburgAdmittedly, in that last example, you’d probably write three methods and useeqto create types corresponding to each color.Lastly, you can now create singletons that are easy to use: just useeqconstructors on a string.Type Types and so on>>> isinstance(Integer, Type)
TrueSee, types can have types (it’s like metatypes, only it makes sense!). In fact,
it actually gets a bit spookier, because:>>> isinstance(Type, Type)
TrueIt’sTypes all the way down!>>> isinstance(Object, Object)
TrueUntil you hit theObjects, that is.A Short Note on Covariance>>> a = [1, 2, 3]
>>> isinstance(a, List(Integer))
True
>>> isinstance(a, List(Complex))
True
>>> a[1] = "asdf"
>>> isinstance(a, List(Integer))
FalseLists (andDictionaries) covary with their contained types. This
raises the usual endless troubles. Cry me a river. Or, better yet, deal with it.