Type Hinting vs Type Checking and Preconditions

Alex Martelli aleaxit at yahoo.com
Thu Mar 9 08:37:00 CET 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