Often when programming we want to evaluate a function only halfway. Consider the following definition of cumulative sum
def cumsum(data):
return accumulate(add, data)
or of fib_many
which applies the Fibonacci function, fib
, onto a list
def fib_many(data):
return map(fib, data)
In each case we specialize a higher order function (accumulate
or map
) with a single specific argument, leaving the second argument open for future use.
# Obligatory set of small functions
from toolz import accumulate
def add(x, y):
return x + y
def mul(x, y):
return x * y
def fib(n):
a, b = 0, 1
for i in range(n):
a, b = b, a + b
return b
# We commonly use a lambda expression and the equals sign
cumsum = lambda data: accumulate(add, data)
# This is perfectly equivalent to the function definition
def cumsum(data):
return accumulate(add, data)
# Or we can use the `partial` function from functools
# Partial inserts an argument into the first place
from functools import partial
cumsum = partial(accumulate, add)
# Semantically like the following:
# cumsum(whatever) = accumulate(add, whatever)
Partially evaluate the mul
function to create a new function double
double = ...
assert double(5) = 10
File "<ipython-input-4-854a2ed35155>", line 1 double = ... ^ SyntaxError: invalid syntax
Currying provides syntacic sugar for partial evaluation.
Curry is a higher order function that changes the behavior of what a function does when it is called with an incomplete set of arguments
Normally Python would raise a TypeError.
Now it returns a partial
mul(2)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-5-74f550b0a831> in <module>() ----> 1 mul(2) TypeError: mul() takes exactly 2 arguments (1 given)
from toolz import curry
mul = curry(mul)
mul(2)
<functools.partial at 0x17c1578>
This allows for the idiomatic expression of functional statements
accumulate = curry(accumulate)
map = curry(map)
cumsum = accumulate(add)
cumprod = accumulate(mul)
fibMany = map(fib)
fibMany(range(10))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
Toolz contains a separate namespace for curried functions
from toolz.curried import map
This is the same as the following:
from toolz import map, curry
map = curry(map)