Overloading operators for currying, a PEP309 suggestion

Lenard Lindstrom PEP308reply at telus.net
Mon Mar 10 22:50:30 EST 2003


In PEP 309 a new operator, '@', was tentatively proposed for currying. But
given that Python allows operator overloading, why not use some existing
operators for currying 'function' objects? The 'function' class has few
operations defined for it, so there remain many to choose from. Here are my
suggestions:

operator '<<' for left currying. Usage:

   <function object> << <argument value>

operator '|' for currying by key. Usage:

   <function object> | (<argument key>, <argument value>) # a tuple
   <function object> | [<argument key>, <argument value>] # a list
   <function object> | {<key> : <value>, ...} # a dict

operator '>>' for right currying, if desired.

Each of the above operators would create a new function object. This may be
a wrapper function calling the curried function or a copy of the curried
function with altered closure and code objects.

The '[]' __getitem__ operator might also be defined for inspection of
argument / value bindings.

Some examples:
>>> fn = lambda x, y, z: [x, y, z]
>>> print (fn << 2 << 4)(6)
[2, 4, 6]
>>> fn2 = fn | {'z':'z value', 'x':'x value'}
>>> print (fn2 | ('y', 'y value'))()
['x value', 'y value', 'z value']

Adding new methods to the 'function' class should not require changes to its
other members or methods.

This test class wraps a function object to overload the '<<', '|', and '()'
operators:

class fnx(object):
    """fnx(fn) => callable object with overloaded operators for currying"""
    def __init__(self, fn, **kwds):
        if not callable(fn):
            raise TypeError, "type not callable: '%s'" % type(fn).__name__
        self._fn = fn
        self._args = kwds.get('_args', ())
        self._kwds = kwds.get('_kwds', {})
    def __lshift__(self, other):
        return fnx(self._fn, _args=self._args + (other,),
_kwds=dict(self._kwds))
    def __or__(self, other):
        if isinstance(other, dict):
            kwds = dict(other)
        elif isinstance(other, list) or isinstance(other, tuple):
            kwds = dict((tuple(other),))
        else:
            raise TypeError, "unsupported operand type(s) for |: 'fnx' and
'%s'" % type(other).__name__
        kwds.update(self._kwds)
        return fnx(self._fn, _args=tuple(self._args), _kwds=kwds)
    def __call__(self, *args, **kwds):
        args = self._args + args
        newkwds = dict(self._kwds)
        newkwds.update(kwds)
        return self._fn(*args, **newkwds)


Lenard Lindstrom
"<%s@%s.%s>" % ("len-l.", "telus", "net")









More information about the Python-list mailing list