Type Hinting vs Type Checking and Preconditions
Alex Martelli
aleaxit at yahoo.com
Thu Mar 9 02:37:00 EST 2006
Jay Parlar <jparlar at cogeco.ca> wrote:
> class A(object):
> def foo(self):
> print "bar"
>
> class B(object):
> def foo(self):
> print "bar"
>
> def typedfunction(x : A):
> x.foo()
>
> b = B()
> typedfunction(b) #Your system would probably consider this an error
>
>
> This is an example of why type checking/hinting is no good, as it would
> break duck typing.
>
> In terms of their definitions, A and B have nothing in common (ie. B
> is not a subclass of A), yet I should be able to use instances of
> either one, whenever the method 'foo' is expected. Type hinting would
> completely break that. This again is why I point you in the direction
> of PEP 246.
Indeed, as 246's paladin, my favorite interpretation of that "x: A"
would be as equivalent to:
def sanefunction(_x):
x = adapt(_x, A)
x.foo()
So the code you give would raise a CantAdaptError, but only because no
adapter has been registered from B to A -- easily fixed non-invasively
e.g. by registering a DuckAdapter, basically to assert that homonimy IS
all right between these classes.
The reason I like the adaptation requirement (rather than explicit
ducktyping being always in force by default) is to avoid the homonimy
problem, a classic issue with large system -- imagine the following
pieces all being independently defined...:
class Lottery(object):
def draw(self): ...
class Artist(object):
def draw(self): ...
...
def runLotteryWithPortraitAsPrize(lottery, artist):
winner = lottery.draw()
portrait = museum.add(artist.draw())
ceremony('Award Prizes', winner, portrtait)
...
runLotteryWithPortraitAsPrize(Artist(), Lottery())
Oops -- "draw" means completely different and unrelated things in
different semantic domains, ducktyping doesn't work well here, and we're
placing (a photo of) the winner in the "museum" (presumably a web photo
gallery;-) and awarding the winner to the portrait, what a mess...!-)
OK, not a realistic usecase;-), but method homonimy may indeed happen,
and in large enough systems it may be a cause of some rare but
hard-to-find bugs -- unittests don't nail them, because they're more in
the nature of system integration problems (until the mistaken call,
everything's hunky dory). Without adaptation -- if your only alternative
was type-testing of some kind -- the inflexibility in gluing together
disparately designed frameworks would probably be too high a price to
pay, and you'd go for ducktyping and take your lumps. But adaptation
(could I but convince Guido about it...!) lets you have most of the
flexibility back (and then some, because you can easily adjust for name
differences too;-) AND still make such problems easy to find (since
they'll raise an exception when they occur).
The nature of what exactly is a "protocol" (what we adapt to) is quite a
secondary issue -- be it a mere interface, a type loosely standing for a
host of semantics implications, an interface+DbC+pragmatics, whatever,
being able to request adaptation (rather than merely checking
compliance, isinstance for types, etc) is STILL a huge gain. Ah well...
Alex
More information about the Python-list
mailing list