[Types-sig] Proposed Goals PEP

Marcin 'Qrczak' Kowalczyk qrczak@knm.org.pl
13 Mar 2001 17:16:46 GMT

Mon, 12 Mar 2001 10:34:44 -0700, Tim Hochberg <tim.hochberg@ieee.org> pis=

> Parameterized types are easy to support in this model, although
> the checks are potentially very expensive.

When a function returns a list of ints and happens to return an empty
list, and another function expects a list of tuples and gets this
empty list, IMHO it should be an error.

This, and the fact that we don't want to iterate over the list
each time it is passed through a typeful gate, suggests that a list
should store the type of its elements in its attribute.

When a list is typechecked for the first time, items are checked
and the type is remembered. From this time the invariant is that it
contains only items of the right type. Further typechecking won't
iterate over elements but look at the stored type. Typechecking of
future items will happen at the time of their insertion.

Similarly, the type of a function cannot be derived from the current
function object (you can't apply the function just to see what type
it will return) - it must be stored in it explicitly.

> Consider a parameterized list type:

I would certainly like to use real type objects and class objects in
type expressions. Unfortunately it creates a syntactic ambiguity when
user-defined parametrized types are involved:
    ClassName(Type1, Type2)
This looks like a constructor call. We don't want this kind of

It can be worked around by using a different syntax for type
application, e.g.
    ClassName[Type1, Type2]

For consistency this extends to builtin types, so we should have
    Tuple[Int, Int, Int]
    Dict[String, Tuple[Int, Int]]

If and only if type expressions were evaluated under different rules,
we could have a nicer syntax:
    (Int, Int, Int)
    {String: (Int, Int)}
But I don't propose this now.

You can ignore type parameters if you wish. Bare Dict means
Dict[Any,Any] (because it is implemented that way).

You can also apply a class object to types and use it as a class.
E.g. UserList() makes a UserList which is typeless yet, but you can
also write UserList[Int]() which will accept only Ints.

Usually you will just write UserList() and wait until it is passed
somewhere where the type is written down, e.g. returned from a
function with the return type of Sequence[String]. The UserList
becomes a UserList of strings then. Applying a type constraint to
a mutable object may change the stored type of its elements.

> class CompositeOf:
>    "A composite type"
>    def __init__(self, *types):
>       self.types =3D types
>       self.name =3D "CompositeOf(%s)" % ", ".join([t.name for t in type=
>    def check(self, object):
>       for t in types:
>          if types.check(t): return 1
>       return 0

You mean a sum type. It can be done using the | operator. When applied
to two types, it produces the right composite type.

Similarly &, which is really type unification. & will not build a
composite type which checks that a value belongs to both types, but
unify types right away, raising an exception when it's not possible.

Typechecking uses unification internally. A list is not in a separate
typeless state initially - it remembers its element type as Any.
Added elements are typechecked wrt. Any, which is trivial (always

When a type constraint is applied to a list, its element type is
unified with that of the constraint. Usually it happens once when
the type is changed from Any to some concrete type (Any & t =3D t),
and many times later when the concrete type is confirmed (t & t =3D t).
The list must typecheck all its elements only when the declared type

So types belong to objects, not to names. You can rebind a local
variable no matter how much its old and new types disagree. But
collections may impose restrictions on types of their elements
and adding an element to a collection is not always type correct.
These restrictions result from explicitly written type constraints
over the collection.

There will be a scheme of declaring types of instance attributes.
The instance will then make sure that a newly assigned object has
a correct type.

 __("<  Marcin Kowalczyk * qrczak@knm.org.pl http://qrczak.ids.net.pl/
  ^^                      SYGNATURA ZAST=CAPCZA