ActiveState Code

Recipe 52902: function composition


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...)

Python
 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)

Discussion

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.

Sign in to comment