Python Type System: An idea for unification

John Skaller skaller at
Sun Aug 29 07:48:36 CEST 1999

At 10:05 28/08/99 -0500, Gordon McMillan wrote:
>John (Max) Skaller wrote:
> [Stuff interesting enough to perhaps revive the Types SIG...]

	.. and some of it is actually implemented!

> [Briefly, and subject to my misinterpretation, it appears that
>  Viper objects always have a type obj; said type obj may be
>  a class instance; said type obj is appended to the normal
>  attribute search path]

	"Usually" the type object will be a class, rather than
an instance.

>>  class PyIntType:
>>   def succ(x): return x + 1
>>   def pred(x): return x - 1
>> To use this we try out the Viper code:
>>  x = 1
>>  print x . succ()
>At first glance, these are class methods. But not really, because the 
>bound method object binds the explicit arg 'x' to the user object (in 
>this case also named 'x'). 

	I'm confused!

	1) the function 'succ' REALLY is an ordinary function.
	2) the class PyIntType REALLY is an ordinary class
	3) the object


	REALLY is an ordinary bound method.

There is NO difference here from Python 1.5.2, it is ONLY the
lookup that is different. [But see below, even the lookup
is exactly what Python does right now!] Try this:

	print [].append

and you will get 

	"<built-in method apppend of list object at 808888>"

so that you can see even in Python 1.5.2, bound methods
do not have to have an 'im_self' object which is a class

	What IS somewhat strange, however is that
'instances' of the TYPE PyIntType are integers,
NOT class instances: an object of CLASS PyIntType:

	object = PyIntType()

is a class instance, it has type Instance, class PyIntType.
Whereas an integer has type PyIntType, and it doesn't
have a class because it is not a class instance.
This is, however, no different from python 1.5.2!

>But that's not

> class PyIntType:
>  def succ(self): return self + 1
>  ...

	Yes, this is exactly equivalent!

>because "self" in this case would be the instance of PyIntType, 

	No: what you mean I think is that

	object = PyIntType()

does not have type integer, and if you call succ:


WOOPS: an error because PyIntType does not have an __add__ method.

>not the user object (which "has a" instance of PyIntType). So _something_ 
>is special here. 

	Correct. The automatic searching of the type
of an object is vaguely special (but not as special
as you think!!)

	However, everything else is stock standard python;
although it is also true that the 'type' of an object
no longer has a type whose type's type is itself
(namely TypeType).

	That is, Python 1.5.2's _internal_ TypeXXXX
objects have been dispensed with, and replaced
by arbitrary objects.

	Note that the normal python TypeXXX object
have method tables, and python normally looks them
up .. so what I am doing is not really that different
to Python 1.5.2!

>Ah, you are actually re-binding (the bound to 
>type(x) method) "succ" to the user object ("1"). Right?

	Yes. This is also what Python 1.5.2 would do,
except the method would come from a special,
C constructed TypeXXXX object.

>>  ListOfInt = ListOf(IntType)
>So ListOfInt is an instance of ListOf. 


>Unbound methods defined in 
>ListOf are bound methods in ListOfInt. 

	Technically, this doesn't make sense: there are
no 'methods', even in Python, in a class, or even
in an instance. A 'method' is created only during
lookup. So, when a lookup is done on an instance,
a function found in the instances class becomes
a bound method, bound to the instance:

	class X:
		def f(x): pass
	x = X()
	g = x.f

Here, f is a function. It is NOT a method, there is no
such thing as a method (except 'in the abstract').
Thus X.__dict__.f is a function, and an ordinary one.

	However, g = x.f is a bound method: a pair
consisting of an object and a function.

	Python 1.5.2 does not require anything
special about the function, nor the object.
The object can be anything, including a list,
and the function can be any function.
(Technically, any code object I think)

>Presumably, they will be 
>rebound to the object using ListOfInt as its type object.

	Yes, but there are TWO bindings. First,
when a CLASS lookup is done for the instance ListOfInt,
a function 'f' in the class dictionary of ListOf
will be bound to the instance ListOfInt. So we have a bound

	<ListOfInt, f>

here represented by a pairing notation. Now, since that
is a callable function, the lookup on an object x will
convert the callable to a bound method a second time:

	<x, <ListOfInt, f>>

A doubly bound method. 

>Is "new_object" a builtin?

	It will need to be, yes.

>One of us is missing something here! There doesn't seem to be anyway 
>to differentiate between:
> class ListOf:
>  def method1(arg1, arg2): ...
>being called from the user object x as
> x.method1()   #generic
>  and/or
> x.method1(somearg) #non generic

	What type is 'x'?

>It seems to me you either need an explicit way of marking them as 
>different when writing ListOf, or (I think more generally), writers 
>of type classes need to always handle 2 "self"s:

	Yes. There will always be two selfs. A better naming
convention is:

	the first one is called 'metaself'
	the second one is called 'self'

With that convention, 'self' refers to the object,
and meta-self refers to the type of the object.
Remember that in a generic type constructed as I have
shown, the methods of the meta-type, in this case
ListOf, ar just that: they're shared by all
the instance types such as ListOf(TypeInt).

So you need to bind one of the arguments to the actual
type object -- the metaself argument -- so that the
method can examine the attributes special to the
particular instance of the generic type.

For example, in:

	m = ListOfInt.method

the m function needs to be bound to ListOfInt,
so, for example, it can find out dynamically
what type it is working with: in the example
I gave, the instance ListOfInt has an 'etype'
attribute which determines the type of element
in the list.

But, the m function needs to be bound to 
the client object -- a list of integers ---
as well, so it can do some work, so

	k = intlist.m

the k function is bound to the object 'intlist'
so it can actually do workk on the object.

> class PyIntType:
>   def succ(self, obj): return obj + 1
>   # "self" is the instance of PyIntType,
>   # "obj" is the "self" of the user object - some obj whose type
>   # object is an instance of PyIntType   
>   ...

	For ordinary types like integers, the 'self'
object is an integer: the PyIntType is not generic,
and the type is a CLASS object, not an INSTANCE
object of the class PyIntType.

	However, you could make a generic integer type,
something like:

	class PyNumberMetaType:
		def succ(metaself, self):
			return metaself.addmethod(self,1)

>Then, of course, you are not rebinding a method; you are binding the 
>2nd arg of an already bound method.

	That is the case when you are using the paradigm:

	class MetaType: ...
		def __init__(args ..)

where the __init__ function constructs an instance type object.

>Cool stuff! 

	Yeah. Just consider: I only examined the cases
where I used a class (for standard object types),
and a class (for meta types). However, 
there is no restriction: the type object can be anything.

	But you know what is REALLY cool?

	This is what Python 1.5.2 does now!
There is only one real difference, and that is that
instead of using a TypeXXX object written in C,
I allow any object, and instead of using the fixed
'vtable' lookup, I just use 'ordinary' lookup.
Note that ordinary Type objects in Python
provide in the vtable, a 'getattr' method
for doing lookups for named attributes.

	Note: Viper doesn't use vtables (or objects).
Instead, the interpreter 'knows' how to do all the
standard operations: the functionality is built
into the interpreter, rather than encapsulated
in a Type object.

	This makes it harder to extend than CPython,
but is necessary for implementing a compiler:
you can't optimise encapsulated methods.

>And you successfully avoided ever using the dreaded 
>"meta" word...

	WOOPS. It seems I didn't. :-)

John Skaller    email: skaller at
		phone: 61-2-96600850
		snail: 10/1 Toxteth Rd, Glebe NSW 2037, Australia

More information about the Python-list mailing list