[Types-sig] Re: Static typing considered ... UGLY
Edward Welbourne <email@example.com>
Tue, 14 Dec 1999 16:18:19 +0000
> too subjective
OK, fair enough: why *do* I find it ugly ?
Substantially, prejudice and paranoia. However ...
If I implement a datatype (probably as a class) whose objects
(instances) behave *just the same as* integers in all pythonic respects,
I demand to be able to use it everywhere that I am allowed to use an
integer. If static typing breaks that, it's right out.
If the way you're doing static typing is based on `what interfaces does
this object support' questions instead of `type' (and I realise your
deliberate vaguery on what you mean by `kind' of value may allow this),
then I'm much less concerned, though I have deep reservations about
changing the syntax of python in order to provide syntactic sugar for
stuff that can, at present, be done using assertions. Furthermore,
boring though it may be to begin a function with as many assertions as
arguments, the assert mechanism leaves ample scope for the programmer to
identify just exactly what it is the programmer wanted to say as the
constraint on the integer (not only is it an integer, and non-negative,
but *it's even*, say): and this without having to invent new and
fascinating syntactic forms to express it.
It's all very well to say that `existing python code will be unaffected'
but if existing python programmers come across
> import frozen
> def foo( a ):
> return string.replace(a,"b")
we're not going to be happy with being expected to understand that foo
is now a name we can't modify. The existing semantics of evaluating an
expression (which is how I'm reading `frozen' the second time it
appears) are that the expression is evaluated and thrown away and doing
so hasn't changed the semantics of how the interpreter modifies
namespaces thereafter. The fact that the last-executed expression
yielded (and discarded) a type object should *not* have any impact on
the meaning of the code following. And existing python programmers
might sensibly write something like:
types.MagicMethodType # Check we're using python 2.0
version = '2'
except AttributeError: # Cope if we're not
version = '1'
and be unhappy about the typerror because '2' isn't a magic method.
Indeed, if any of the 1.x chain have added values to types, the above
code may appear awful close to verbatim in reality ...
On the other hand, if you want an object whose attributes are of
pre-decided kinds, or a namespace in which certain names are reserved
for certain values, use a setattr hack (or, if you're feeling very
brave, some variant on the wrapper defined by
Likewise, if you want a namespace (the module in which your code above
appeared) which can be initialised `in the usual way' but which (except
with severe hassle which should alert folk to the folly of doing so)
can't be modified after initialisation, use an initspace ...
see .../~eddy/dev/toy/object.html and, in the same directory, python.py
> if Python is never allowed to make major changes ...
I'm not suggesting `no change' - only `not in that direction'.
And even type-checking can get past my prejudices if it's approached
... I've now read the Greg/Fred/Sjoerd attack and I like that: let !
be a new binary operator with grammar
anyvalue ! typechecker
the value of the expression being that of the given value, but
evaluating it'll raise an exception if the typechecker didn't like the
value. Now that's a much nicer way to go. Of course, this effectively
just amounts to implementing ! as an in-expression assert mechanism ...
and I'm not entirely sure how it helps the compiler-writer - is that why
you insist on the typechecker being a dotted name, not an arbitrary
Type-checking applies to values ;^)
Of course, obstreperous as I am, I immediately want to meddle with the
scheme: specifically, though the *default* behaviour might be (in
if not isinstance(value, typechecker): raise TypeError
else: yield value
I'd argue for the semantics to say: evaluate the expressions `value' and
`typechecker', look for a __check__ method on the latter: if present
invoke it on the value, else use isinstance as above; on false return
(no problem) the !-expression yields the given `value', otherwise
TypeError with parameter the true value returned. Then we can implement
weird and devious __check__ methods for fiddly type-checks (instead of
needing to change isinstance in the way proposed - which would conflict
with my pet tweak to that, which allows isinstance(value, thistype,
thattype, othertype) for when I've got several types I'll accept).
Please Greg/Fred/Sjoerd, can you write a proposal which starts where
ends (reading the first 9/10 of that was ... illuminating in hindsight,
but off-putting on the way there). It looks pretty promising ...