These two classes show two styles of function composition. The difference is only when the second function (g) returns a tuple. compose passes the results of g as a tuple, mcompose treats it as a tuple of args to pass along. Note that extra args provided to (m)compose are treated as extra args to f (there is no standard functional behavior here to follow).
compose(f,g, x...)(y...) = f(g(y...), x...)
mcompose(f,g, x...)(y...) = f(*g(y...), x...)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | class compose:
'''compose functions. compose(f,g,x...)(y...) = f(g(y...),x...))'''
def __init__(self, f, g, *args, **kwargs):
self.f = f
self.g = g
self.pending = args[:]
self.kwargs = kwargs.copy()
def __call__(self, *args, **kwargs):
return self.f(self.g(*args, **kwargs), *self.pending, **self.kwargs)
class mcompose:
'''compose functions. mcompose(f,g,x...)(y...) = f(*g(y...),x...))'''
TupleType = type(())
def __init__(self, f, g, *args, **kwargs):
self.f = f
self.g = g
self.pending = args[:]
self.kwargs = kwargs.copy()
def __call__(self, *args, **kwargs):
mid = self.g(*args, **kwargs)
if isinstance(mid, self.TupleType):
return self.f(*(mid + self.pending), **self.kwargs)
return self.f(mid, *self.pending, **self.kwargs)
|
As in curry, these functions are for constructing functions from other functions. Your guideline for use should be clarity, since there is no efficiency gained by using the functional forms.
A quick example for interactive use:
parts = compose(' '.join, dir)
This function applied to a module gives you an easy-to-view list of the module's contents.
I separated the mcompose and compose since I think of the two possible forms of function composition quite differently.