Guido asked me to move it here. anyway, i might as well state my case better.
in the formal definitions of object oriented programming, objects
are said to encapsulate state and behavior. behavior is largely dependent
on the type of the object, but the state is important nonetheless.
for example, file objects are stateful: they can be opened for reading-only,
writing-only, both, or be closed altogether. still, they are all instances
of the file type.
since generic functions/multi-dispatch come to help the programmer
by reducing boilerplate early type-checking code and providing a
more capable dispatch mechanism -- we can't overlook the state of the
this early type-checking goes against the spirit of pure duck typing (which
i'm fond of), but is crucial when the code has side effects. in this case,
you can't just start executing the code and "hope it works", as the
resources (i.e., files) involved are modified. in this kind of code, you
want to check everything is alright *instead* of suddenly having
an AttributeError/TypeError somewhere.
here's an example:
def copy(src: file, dst: file):
buf = src.read(1000)
if not buf: break
suppose now that dst is mistakenly opened for reading.
this means src would have already been modified, while
dst.write is bound to fail. if src is a socket, for instance,
this would be destructive.
so if we already go as far as having multiple dispatch, which imposes
constraints on the arguments a function accepts, we might as well
base it on the type and state of the object, rather than only on it's type.
we could say, for example:
def copy(src: file_for_reading, dst: file_for_writing):
file_for_reading is not a type -- it's a checker. it may be defined as
def file_for_reading(obj: file):
return file.mode == "r" and not
types, by default, would check using isinstance(), but costume
checkers could check for stateful requirements too.
and a note about performance: this check is required whether
it's done explicitly or by the dispatch mechanism, and since
most functions don't require so many overloads, i don't think
it's an issue.
besides, we can have a different decorator for dispatching
by type or by checkers, i.e., @dispatch vs @stateful_dispatch,
or something. the simple @dispatch would use a dictionary,
while the stateful version would use a loop.
---------- Forwarded message ----------
tomer filiba <email@example.com>
Date: Jan 14, 2007 2:28 PM
Subject: multi-dispatch again
i just thought of a so-to-speak counter-example for ABCs... it's not really
a counter-example, but i believe it shows a deficiency in the concept.
theoretically speaking, objects are a made of type and state. ABCs, isinstance()
and interfaces at general only check the type part. for example:
def log_to_file(text: str, device: file):
this will constrain the *type* of the device, but not its *state*.
practically speaking, i can pass a closed file, or a file open for reading-only,
and it would pass silently.
basing multi-dispatch on types is of course a leap forward, but if we
already plan to take this leap, why not make it general enough to support
more complex use-cases?
this way we could rewrite the snippet above as
def log_to_file(text: str, device: open_file):
where open_file isn't a type, but rather a "checker" that may also examine the
state. by default, type objects would check for inheritance (via a special method),
but checkers could extend this behavior.
for efficiency purposes, we can have two decorators:
@type_dispatch - dispatches based on type only
@full_dispatch - dispatches based on type and state
bottom line -- we can't just look at the type of the object for dispatching,
overlooking its state. the state is meaningful, and we'd want the function
not to be called at all if the state of the object is wrong.