[Types-sig] Sample declarations

Guido van Rossum guido@digicool.com
Tue, 13 Mar 2001 20:21:23 -0500

> > - Your test for number assumes that something can be converted to
> >   complex.  That may not be true.
> What's an appropriate definition for number? operator.isNumberType()?

No, the situation is approximately as murly as for sequences, mappings
or files.

> > - Your test for Integer includes float/complex numbers with integral
> >   values; that doesn't work, e.g. a[1.0] raises TypeError if a is a
> >   sequence.
> And yet this works:
> open("c:\\autoexec.bat", "r", 5.0).read()
> Is it by design that these two things work differently? Or is it an
> implementation quirk? 

It's an implementation quirk that this works -- it really shouldn't!
Note that even 5.1 as the buffer size works -- and *that* *really*
shouldn't be allowed.

The deep reason is that if a type supports conversion to integer, you
don't know if it's widening from a smaller type of int, or narrowing
from e.g. float.

> And if it is an implementation quirk then maybe we should use this as an
> opportunity to make the language more consistent by loosening up some
> type checks.

Or tightening, depending on what feels right.

If and when the numeric unification is introduced, I'm all for
allowing 5.0 as buffer size (but disallowing 5.1).

Until that all is in place, I'm more comfortable with requiring that a
Python int is given when the C code needs a C int.

> My impression is that PyArg_ParseTuples is pretty flexible and it made
> sense for the Python equivalent of PyArg_ParseTuples to be as similar as
> possible. Of course one big definition between PyArg_ParseTuples and
> __implementedby__ is that the former actually coerces its args and the
> latter only checks them. Maybe this is a design flaw....

Yes, the flaw is in the current PyArg_ParseTuple() implementation.

> If there is an obvious way to coerce an object to a type, we should
> probably use it. This will make the whole system's behavior more
> predictable. But this doesn't work:
>  coerce(5, types.FloatType)
> In general I don't know how to try to coerce objects to types. I only
> know how to coerce two objects to some common type.

The coerce() function is irrelevant here -- it takes two objects and
returns a tuple of the two coerced to a common type.

What you want is a cast.  We have cast functions named after the
concrete types, e.g. int(), float(), long(), etc.

> > - Your test for Float values that accepts ints is also bad news, at
> >   least until integer division is fixed.  You may want to define a
> >   separate type IntOrFloat, for those cases where the programmer
> >   asserts that either type is fine, and another IntOrFloatOrComplex.
> Not entirely happy with that design!!! I'd rather change the language
> (which we already intend to fix) rather than hack up the type system.

OK, but then the burden is on you to propose a way to fix the language
that can be introduced without breaking existing code -- or else you
will have to walk the slow and tedious way of all incompatible
language changes, and that would get in the way of experimenting with
type annotations "soon".

> > > def findInterface(iface, obj):
> > >     ifaces = getattr(obj, "__interfaces__", None)
> > >     # don't make this mutually recursive on
> > >     # Sequence.__implementedby__!
> > >     if type(ifaces) in (types.ListType, types.TupleType):
> > >         return iface in ifaces # multiple interfaces case
> > >     else:
> > >         return iface is ifaces # single interface case
> > 
> > This helper function is steeped in irrelevant implementation detail!
> I'm implementing something! I have to put in all of the details required
> to make it work. It's supposed to be a prototype not pseudocode.

Hm, everything else appeared so simplistic that it looked more as if
it was pseudo-code.  But I see your point.  Keep trying!

> > - Your assumptions about sequences, mappings etc. are also pretty
> >   naive.  operator.isSequenceType() and .isMappingType() return true
> >   for all class instances regardless of what they actually implement,
> >   and should this be avoided.
> I had an explicit check for InstanceType-ness but accidentally deleted
> it. I thought I had incorporated it into findInterface. I'll put it back
> in. 
> Here's Sequence, fixed up:
> class _sequence:
>     "Any kind of sequence"
>     def __implementedby__(self, obj):
>         if findInterface(self, obj): # if an object claims to support
>             return 1                 # the protocol, we trust it!
>         elif type(obj)==types.InstanceType:  # no safe way to know if an
> instance
>             return 0                   # supports the protocol 
>         else:
>             return operator.isSequenceType(obj) # safe for C-types
> Sequence=_sequence()

Or for instance you could assume it's a sequence if it has
__getitem__.  This guesses wrong when it wants to be a mapping, but I
believe it's better to accept too much than to reject too much here
(since it's clear that we aren't going to get it right 100% anyway --
we don't want to break correct code).

> > - One way to go would be to have explicit ways to say "x is any
> >   Sequence", "x is a List", "x is any Mapping", "x is a Dictionary".
> We do:
> def foo(x: Sequence, x:ListType, x:Mapping, x:DictionaryType): ...


> I haven't implemented the part that checks type objects (or class
> objects, for that matter). Here's a new version with an implementation
> for that:
> def experimentalAssertTypes(dict):
>     # this is VERY ROUGH
>     # for one thing it should use variable names from the caller,
>     # not a passed in dictionary!

Can I make a comment on these comments?  You seem to be using comments
only when there's something not 100% kosher in your code.  An implied
"XXX". :-) I'd appreciate to see more comments that actually explain
what you are doing and why, rather than what you are *not* (yet)
doing!  (Most of JPython was once written using this convention --
while it was clean code, it was very hard to read for the second

>     for val, typ in dict.items():
>         if type(typ) in (types.ClassType, types.TypeType):
>             if not isinstance(val, typ):
>                 raise TypeError(val, typ)
>         elif typ.__implementedby__(val):
>             continue
>         else:
>             raise TypeError(val, typ)
> > - But note again that Sequence-ness and Mapping-ness are ill-defined.
> I agree. This comes back to the issue of interfaces. I think that the DC
> guys have made some progress on a standard type hierarchy, haven't they?

Not that I know of.  I've seen an UML diagram purporting to be "the
Python type herarchy" but it has all of the same flaws your first
attempt exposed, and a lot more time was spent. :-(

> > In short, it looks (and I am as surprised as you are!!!) like we will
> > need to agree on the Python type hierarchy before we can ever hope to
> > agree on any kind of type assertions.  
> Speak for yourself! :) I've tried to define this type hierarchy twice. I
> decided to pursue with Tim's suggestion because it provides a powerful
> (Turing-complete!) way of defining these types. In my opinion,
> typecheck.py is our type definition document for now. We'll haggle over
> the definitions of the classes and then we'll write a PEP that reflects
> our decisions.

OK, fair enough!

> It sounds backwards but interface defintion always requires a formal
> syntax (IDL, ODL, PyDL). In this case our formal syntax is Python
> methods...

Good luck!

--Guido van Rossum (home page: http://www.python.org/~guido/)