I'm slowly trying to get into functional programming in Python and came across the following problem:
Given two functions f1 and f2, how can I construct a function f that multiplies these two functions with the same argument 'in a functional way'?
Not having dived too much into functional programming I do have one solution
f = lambda x : f1(x) * f2(x)
but it somehow didn't seem to be in the right spirit of functional programming.
My next attempt was to use the mul and the juxt operators like this
>>> from tools import juxt
>>> from operator import mul
>>> f = mul(juxt(f1,f2))
TypeError: op_mul expected 2 arguments, got 1
Trying to split the tuple output of juxt with * didn't work either:
>>> f = mul(*juxt(f1, f2))
TypeError: mul() argument after * must be an iterable, not juxt
Using lambda again seemed to work, but it somehow defeated the whole purpose...
>>> temp = juxt(f_1, f_2)
>>> f = lambda x : mul(*temp(x))
Maybe I'm being too pedantic or unthankful (to Python) here, but I feel like I'm missing something very essential or routine in functional programming.
Is there a more functional way of doing this?
TL;DR Such composition is a primitive (in the sense that it cannot be decomposed into other higher-order functions) operation that neither Python nor the
toolsmodule supports. You need to implement it yourself.What you are missing (or rather, what Python and the
toolsmodule are missing) is the notion of an applicative functor. To understand what that means, let's first review two functions in thetoolsmodule:composelets you chain together two functions. That is,curryis related to partial application: a demonstration will be quicker than an explanation:That is,
curry(f)(x)is basically the same aspartial(f, x); both take a valueyto return the valuef(x, y).In addition, a functor is basically a way to map a function over some value. You are no doubt familiar with the list functor:
Functions are also functors, but instead of
map, we usecompose. That is, mappingfovergproducescompose(f, g).Now, to combine
mul,f1, andf2intog = lambda x: g(f1(x), f2(x)), it seems like bothcomposeandcurrywould be useful. To wit,and
(That is,
curryis what allows us to compose a two-argument function with another function.)But composition is, in some sense, strictly a linear operation; the input of one function comes from the output of another. The composition of
mulandf1creates a function that expects an argument and returns a function that expects that same argument. How do we movexout of the "middle" of either expression? What we need is some mystery functionfoosuch thatthat makes a function that passes its argument to both
fandg, while also passing the resultg(x)tof. With such a functionfoo, we could writeand get our desired result.
And that brings us to the idea of an applicative functor. It provides the necessary function
foothat combines the notions of composition and currying that we don't currently have.
In other words,
foois a distinct primitive operation; you can't implement it in terms of composition itself.