does lack of type declarations make Python unsafe?

Bengt Richter bokr at oz.net
Thu Jun 19 16:03:39 EDT 2003


On Wed, 18 Jun 2003 22:57:08 -0400, David Abrahams <dave at boost-consulting.com> wrote:

>bokr at oz.net (Bengt Richter) writes:
>
>> On Wed, 18 Jun 2003 10:25:54 -0400, David Abrahams <dave at boost-consulting.com> wrote:
>>
>>>Alex Martelli <aleax at aleax.it> writes:
>>>
>>>> But this has little to do with the need of 'type declarations'.  I
>>>> suspect that a statically typed language would also be better off
>>>> without them, relying on type inferencing instead, a la Haskell (and
>>>> Haskell's typeclasses to keep the inferencing as wide as feasible),
>>>> for example.  But I have no research to back this up;-).
>>>
>>>I don't have any first-hand experience, but the experience of friends
>>>of mine who have used Haskell is that it can be exceedingly difficult
>>>to locate the source of a type error when it does occur, since the
>>>inference engine may propagate the "wrong" type back much further than
>>>the source of the error.
>>>
>>>Furthermore, if you do everything by inference you lose the
>>>explanatory power of type declarations.
>>>
>> But what does "type" really mean? ISTM that in the static typing world
>> "type" mostly identifies a type of *representation* -- i.e., things that
>> have compatible memory layouts. 
>
>That's a very primitive notion of type, and not at all what is meant
>in type theory.
>
Ok. I was focusing on an aspect of practical type implementation rather
than associated semantics. The wider notion is type as ability to play
a role correctly in terms of operations on/with objects that are instances of
the type, noting that a type has a type that itself plays roles in operations
of defining composite types from primitive ones and others.

>> Of course any Python implementation will have underlying
>> *representations* of its entities, but those are not a central
>> concern of Python or Python programmers. In fact, UIAM it is a goal
>> of Python design to enable programmers to forget about concrete
>> representations as much as possible, except for "foreign"
>> interfacing.
>>
>> I.e., the "types" of Python are more abstract, and are more about
>> abstract structure and functional compatibility than underlying
>> representation. ISTM "static" typing in this world can't be a direct
>> translation of the old concept, unless you are talking about an interface
>> to an old-style "statically typed" world, or want to incorporate
>> an encapsulated chunk of that into python.
>
>I think most of the above is answered in my reply to Alex, which I
>refer you to.
An interesting exchange ;-) It got me to thinking that if you could subclass
dict and tuple to enforce type constraints, and then use them to make
function local namespaces and arg/return value tuples, it might be interesting.
E.g., for the dict, (just a sketch to illustrate functionality, obviously not
an optimized implementation):

class tydict(dict):
    def __new__(cls, *args, **kw):
        self = dict.__new__(cls, *args)
        self.types = kw
        return self
    def __init__(self, *args, **kw):
        dict.__init__(self, *args)
    def __setitem__(self, key, value):
        if self.types:
            if not key in self.types:
                raise TypeError,'tydict instance at 0x%x: %r is not acceptable key' % (
                    id(self), key)
            if not isinstance(value, self.types[key]):
                raise TypeError,'tydict instance at 0x%x: %r may not be set to type %s' % (
                    id(self), key, type(value).__name__)
        dict.__setitem__(self, key, value)
    def settypes(self, **kw):
        self.types = kw
    def update(self, dct):
        for key in dct.keys(): self[key] = dct[key]
    def setdefault(self, key, value):
        if key in self: return self[key]
        self[key]=value
        return value
    def copy(self):
        return self.__class__(self, **self.types)
 
This would give a kind of optional assert_on_use for a dict. You could enhance the type info
to include const to make the first binding stick, etc., or restrict only the named keys but
allow others to added. Of course, you would have to get in at low level uses of dicts to affect
module globals and object attributes etc. Maybe if a settypes method were implemented for the
various dict incarnations to turn on checks as above it could be done.

Tough to do without performance hits though, I guess. Except if a function could be munged a la
classmethod etc, and the info passed there, it might guide a second pass of type inference pretty
tightly and create opportunity for optimization.

I was thinking if there were a (meta?) class typle which implemented definable tuple types,
where there was a corresponding tuple of types playing an analogous role to tydict.types
in defining the allowable types for each position, then that might be used for argument and
function return tuples somehow, but I haven't pursued that beyond this ;-)

Anyway, between tydict and typle, you could dictate a lot about how "variables" were used.

Small demo of tydict (not tested beyond this!!):

 >>> while s!='quit':
 ...     s = raw_input('test> ')
 ...     try:
 ...         exec s
 ...     except Exception,e:
 ...         print '%s: %s'%(e.__class__.__name__, e)
 ...
 test> td = tydict(x=int, y=str)
 test> print td
 {}
 test> td['a']=3
 TypeError: tydict instance at 0x7a6990: 'a' is not acceptable key
 test> td['x']=3
 test> print td
 {'x': 3}
 test> td['y']='four'
 test> print td
 {'y': 'four', 'x': 3}
 test> td['x']='three'
 TypeError: tydict instance at 0x7a6990: 'x' may not be set to type str
 test> td['y']=4
 TypeError: tydict instance at 0x7a6990: 'y' may not be set to type int
 test> print td.setdefault('x','three')
 3
 test> del td['x']
 test> print td.setdefault('x','three')
 TypeError: tydict instance at 0x7a6990: 'x' may not be set to type str
 test> td2 = td.copy()
 test> td2.update({'x':333, 'y':4})
 TypeError: tydict instance at 0x7a20d0: 'y' may not be set to type int
 test> td2.update({'x':333})
 test> print td2
 {'y': 'four', 'x': 333}
 test> print td2.types
 {'y': <type 'str'>, 'x': <type 'int'>}
 test> td2.settypes()
 test> print td2.types
 {}
 test> td2['a']=8
 test> print td2
 {'y': 'four', 'x': 333, 'a': 8}
 test> quit

Just messing around, don't take too seriously ;-)

Regards,
Bengt Richter




More information about the Python-list mailing list