tags:

views:

232

answers:

3

A generic function is dispatched based on the type of all its arguments. The programmer defines several implementations of a function. The correct one is chosen at call time based on the types of its arguments. This is useful for object adaptation among other things. Python has a few generic functions including len().

These packages tend to allow code that looks like this:

@when(int)
def dumbexample(a):
    return a * 2

@when(list)
def dumbexample(a):
    return [("%s" % i) for i in a]

dumbexample(1) # calls first implementation
dumbexample([1,2,3]) # calls second implementation

A less dumb example I've been thinking about lately would be a web component that requires a User. Instead of requiring a particular web framework, the integrator would just need to write something like:

class WebComponentUserAdapter(object):
    def __init__(self, guest):
        self.guest = guest
    def canDoSomething(self):
        return guest.member_of("something_group")

@when(my.webframework.User)
componentNeedsAUser(user):
    return WebComponentUserAdapter(user)

Python has a few generic functions implementations. Why would I chose one over the others? How is that implementation being used in applications?

I'm familiar with Zope's zope.component.queryAdapter(object, ISomething). The programmer registers a callable adapter that takes a particular class of object as its argument and returns something compatible with the interface. It's a useful way to allow plugins. Unlike monkey patching, it works even if an object needs to adapt to multiple interfaces with the same method names.

+5  A: 

I'd recommend the PEAK-Rules library by P. Eby. By the same author (deprecated though) is the RuleDispatch package (the predecessor of PEAK-Rules). The latter being no longer maintained IIRC.

PEAK-Rules has a lot of nice features, one being, that it is (well, not easily, but) extensible. Besides "classic" dispatch on types ony, it features dispatch on arbitrary expressions as "guardians".

The len() function is not a true generic function (at least in the sense of the packages mentioned above, and also in the sense, this term is used in languages like Common Lisp, Dylan or Cecil), as it is simply a convenient syntax for a call to specially named (but otherwise regular) method:

len(s) == s.__len__()

Also note, that this is single-dispatch only, that is, the actual receiver (s in the code above) determines the method implementation called. And even a hypothetical

def call_special(receiver, *args, **keys):
    return receiver.__call_special__(*args, **keys)

is still a single-dispatch function, as only the receiver is used when the method to be called is resolved. The remaining arguments are simply passed on, but they don't affect the method selection.

This is different from multiple-dispatch, where there is no dedicated receiver, and all arguments are used in order to find the actual method implementation to call. This is, what actually makes the whole thing worthwhile. If it were only some odd kind of syntactic sugar, nobody would bother with using it, IMHO.

from peak.rules import abstract, when

@abstract
def serialize_object(object, target):
    pass

@when(serialize_object, (MyStuff, BinaryStream))
def serialize_object(object, target):
    target.writeUInt32(object.identifier)
    target.writeString(object.payload)

@when(serialize_object, (MyStuff, XMLStream))
def serialize_object(object, target):
    target.openElement("my-stuff")
    target.writeAttribute("id", str(object.identifier))
    target.writeText(object.payload)
    target.closeElement()

In this example, a call like

serialize_object(MyStuff(10, "hello world"), XMLStream())

considers both arguments in order to decide, which method must actually be called.

For a nice usage scenario of generic functions in Python I'd recommend reading the refactored code of the peak.security which gives a very elegant solution to access permission checking using generic functions (using RuleDispatch).

Dirk
What about http://pypi.python.org/pypi/simplegeneric also by PJE?
joeforker
AFAIK this is a pretty simple implementation, which does only type-based dispatch. I haven't really looked into it, so I cannot say anything about it. My experiences are mostly with `RuleDispatch` and its successor.
Dirk
simplegeneric is FAR simpler, but, I think, not as actively maintained as the more complex and richer PEAK.
Alex Martelli
A: 

You can use a construction like this:

def my_func(*args, **kwargs):
    pass

In this case args will be a list of any unnamed arguments, and kwargs will be a dictionary of the named ones. From here you can detect their types and act as appropriate.

defrex
This doesn't make sense in the context of the revised question.
S.Lott
Ya, it seemed like the right answer at the time.
defrex
A: 

I'm unable to see the point in these "generic" functions. It sounds like simple polymorphism.

Your "generic" features can be implemented like this without resorting to any run-time type identification.

class intWithDumbExample( int ):
    def dumbexample( self ):
        return self*2

class listWithDumbExmaple( list ):
    def dumpexample( self ):
        return [("%s" % i) for i in self]

def dumbexample( a ):
    return a.dumbexample()
S.Lott
Your approach does not allow calls to `dumbexample(1)` as per the OP's specs (and as all generic function packages do allow) -- you need cumbersome wrapping on each such call, typically with switching based on types (eep). But polymorphism based on ONE type is still roughly OK: where classic OOP utterly crumbles and generic functions (as in Common Lisp, Dylan, ...) truly shine is in dispatching based on MULTIPLE arguments' types (avoiding klutzes such as Visitor, __radd__ vs __add__, and a zillion other kludges OOP needs there).
Alex Martelli
@Alex Martelli: And, presumably, avoiding the coerce issues and double-dispatch issues. The OP's question -- and example -- didn't touch on any of this. I'm still unclear on the *real* question. Or perhaps the question was just hypothetical.
S.Lott