Next Chapter: Finite State Machine in Python

## Currying

### General Idea

In mathematics and computer science, currying is the technique of breaking down the evaluation of a function that takes multiple arguments into evaluating a sequence of single-argument functions.

Currying is not only used in programming but in theoretical computer science as well. The reason is that it is often easier to transform multiple argument models into single argument models.

The need for currying arises for example in the following case: Let us assume that we have a context, in which we can only use a function with one argument. We have to use a function with multiple parameters. So we need a way to transform this function into a function with just one parameter. Currying provides the solution to this problem. Currying means rearranging a multiple-parameter function into a chain of functions applied to one argument. It is always possible to transform a function with multiple arguments into a chain of single-argument functions.

Python is not equipped for this programming style. This means there are no special syntactical constructs available to support currying. On the other hand, Python is well suited to simulate this way of programming. We will introduce various ways to accomplish this in this chapter of our tutorial.

Some readers not interested in the mathematical details can skip the following two subchapters, because the a mathematically focussed.

### Composition of Functions

We define the composition h of two function f and g

$$h(x) = g(f(x)$$often written as

$$h = (g \circ f)(x)$$in the following Python example.

The composition of two functions is a chaining process in which the output of the inner function becomes the input of the outer function.

### Currying

As we have already metnioned in the introduction, currying means transforming a function with multiple parameters into a chain of functions with one parameter.

We will start with the simplest case, i.e. two parameters. Given is a function $f$ with two parameters $x$ and $y$. We can curry the function in the following way:

We have to find a function $g$ which returns a function $h$ when it is applied to the second parameter $y$ of $f$. $h$ is a function which can be applied to the first parameter $x$ of $f$, satisfying the condition

$$f(x, y) = g(y)(x) = h(x)$$Now we have a look at the general case of currying. Let us assume that we have a function $f$ with $n$ parameters:

$$f(x_1, x_2, \dots x_n)$$Currying leads to a cascade of functions:

$$f_{n-1} = f_n(x_n)$$$$...$$$$f_1 = f_2(x_2)$$$$f(x_1, x_2, \dots x_n) = f_1(x_1)$$```
def compose(g, f):
def h(x):
return g(f(x))
return h
```

We will use our compose function in the next example. Let's assume, we have a thermometer, which is not working accurate. The correct temperature can be calculated by applying the function *readjust* to the temperature values. Let us further assume that we have to convert our temperature values into degrees fahrenheit. We can do this by applying *compose* to both functions:

```
def celsius2fahrenheit(t):
return 1.8 * t + 32
def readjust(t):
return 0.9 * t - 0.5
convert = compose(readjust, celsius2fahrenheit)
convert(10), celsius2fahrenheit(10)
```

The composition of two functions is generally not commutative, i.e. compose(celsius2fahrenheit, readjust) is different from compose(readjust, celsius2fahrenheit)

```
convert2 = compose(celsius2fahrenheit, readjust)
convert2(10), celsius2fahrenheit(10)
```

*convert2* is not a solution to our problem, because it is not readjusting the original temperatures of our thermometer but the transformed Fahrenheit values!

#### "compose" with an Arbitrary Number of Arguments

The function *compose* which we have just defined can only cope with single-argument functions. We can generalize our function *compose* so that it can cope with all possible functions. This is not currying of course but nevertheless also an interesting function.

```
def compose(g, f):
def h(*args, **kwargs):
return g(f(*args, **kwargs))
return h
```

Example using a function with two parmameters.

```
def BMI(weight, height):
return weight / height**2
def evaluate_BMI(bmi):
if bmi < 15:
return "Very severely underweight"
elif bmi < 16:
return "Severely underweight"
elif bmi < 18.5:
return "Underweight"
elif bmi < 25:
return "Normal (healthy weight)"
elif bmi < 30:
return "Overweight"
elif bmi < 35:
return "Obese Class I (Moderately obese)"
elif bmi < 40:
return "Obese Class II (Severely obese)"
else:
return "Obese Class III (Very severely obese)"
f = compose(evaluate_BMI, BMI)
weight = 1
while weight > 0:
weight = float(input("weight (kg) "))
height = float(input("height (m) "))
print(f(weight, height))
```

### BMI Chart

This is off-topic, because it is not about currying. Yet, it is nice to have a BMI chart, especially if it is created with Python means. We use contour plots from the Matplotlib module in our program. If you want to learn more about contour plots, you can go to chapter on Contour Plots of our Matplotlib tutorial.

```
%matplotlib inline
import matplotlib.pyplot as plt
import pylab as pl
xlist = pl.linspace(1.2, 2.0, 50)
ylist = pl.linspace(50, 100, 50)
X, Y = pl.meshgrid(xlist, ylist)
Z = Y / (X**2)
plt.figure()
levels = [0, 15, 16, 18.5, 25, 30, 35, 40, 100]
cp = plt.contour(X, Y, Z, levels)
pl.clabel(cp, colors = 'k', fmt = '%2.1f', fontsize=12)
c = ('#0000FF', '#0020AA', '#008060', '#00AA40', '#00FF00', '#40AA00', '#992200', '#FF0000')
cp = plt.contourf(X, Y, Z, levels, colors=c)
plt.colorbar(cp)
plt.title('Contour Plot')
plt.xlabel('x (m)')
plt.ylabel('y (kg)')
plt.show()
```

### Currying Examples in Python

#### Currying BMI

We used the function BMI in a composition in the previous example. We will now use it as a first currying example. The height of grown-ups is principally a constant. Okay, I know, we shrink every day from dawn to dusk about one centimetre and we get the loss returned over night. We define a function *f* which takes a height and returns a function whith one parameter (weight) to return the BMI:

```
def BMI_weight(height):
def h(weight):
return weight / height**2
return h
for weight in [60, 68, 74]:
for height in [164, 168, 172]:
print(BMI_weight(height)(weight), BMI(weight, height))
```

#### Example: Currency Conversion

In the chapter on Magic Functions of our tutorial we had an excercise, in which we defined a class for currency conversions.

We will define now a function *exchange*, which takes three arguments:

- The source currency
- The target currency
- The amount in the source currency

To function needs the actual exchange rates. We can download them from finance.yahoo.com website with the function get_currencies. Though in our example we use some old exchange rates:

```
currencies = {'CHF': 1.0821202355817312,
'CAD': 1.488609845538393,
'GBP': 0.8916546282920325,
'JPY': 114.38826536281809,
'EUR': 1.0,
'USD': 1.11123458162018}
```

```
def exchange(from_currency, to_currency, amount):
result = amount * currencies[to_currency]
result /= currencies[from_currency]
return result
exchange("CHF", "CAD", 100)
```

We can now define *curried* functions from the function *exchange*:

```
def exchange_from_CHF(to_currency, amount):
return exchange("CHF", to_currency, amount)
def CHF2EUR(amount):
return exchange_from_CHF("EUR", amount)
print(exchange_from_CHF("EUR", 90))
print(CHF2EUR(90))
```

We want to rewrite the function *exchange* in a curryable version:

```
def curry_exchange(from_currency=None,
to_currency=None,
amount=None):
if from_currency:
if to_currency:
if amount:
def f():
return exchange(from_currency, to_currency, amount)
else:
def f(amount):
return exchange(from_currency, to_currency, amount)
else:
if amount:
def f(to_currency):
return exchange(from_currency, to_currency, amount)
else:
def f(to_currency=None, amount=None):
if amount:
if to_currency:
def h():
return exchange(from_currency, to_currency, amount)
else:
def h(to_currency):
if to_currency:
return exchange(from_currency, to_currency, amount)
else:
if to_currency:
def h(amount):
return exchange(from_currency, to_currency, amount)
else:
def h(to_currency, amount):
return exchange(from_currency, to_currency, amount)
return h
else:
def f(from_currency, to_currency, amount):
return exchange(from_currency, to_currency, amount)
return f
```

We can redefine *exchange_from_CHF* and *CHF2EUR* in a properly curried way:

```
exchange_from_CHF = curry_exchange("CHF")
print(exchange_from_CHF("EUR", 90))
CHF2EUR = curry_exchange("CHF", "EUR")
print(CHF2EUR(90))
```

You will find various calls to curry_exchange in the following examples:

```
print(curry_exchange("CHF")( "EUR", 100))
print(curry_exchange("CHF", "EUR")(100))
f = curry_exchange("CHF")
print(f("EUR", 100))
g = f("EUR")
print(g(100))
CHF2EUR= curry_exchange("CHF", "EUR")
print(CHF2EUR(100))
k = curry_exchange("CHF", "EUR", 100)
print(k())
print(curry_exchange("CHF", "EUR", 100))
f = curry_exchange("CHF")(amount=100)
print(f("EUR"))
f = curry_exchange("CHF")
print(f("EUR", 100))
f = curry_exchange("CHF")
g = f("EUR")
print(g(100))
g2 = f(amount=120)
for currency in currencies:
print(currency, g2(currency))
```

```
def arimean(*args):
return sum(args) / len(args)
def curry(func):
f_args = []
f_kwargs = {}
def f(*args, **kwargs):
nonlocal f_args, f_kwargs
if args or kwargs:
f_args += args
f_kwargs.update(kwargs)
return f
else:
return func(*f_args, **f_kwargs)
return f
s = curry(arimean)
s(2)(5)(9)(4, 5)
s(5, 9)
print(s())
s2 = curry(arimean)
s2(2)(500)(9)(4, 5)
s2(5, 9)
s2()
def add(x, y, z):
return x + y + z
s2 = curry(add)
s2(3, 5)(8)()
```

```
def exchange(from_currency, to_currency, amount):
result = amount * currencies[to_currency]
result /= currencies[from_currency]
return result
e = curry(exchange)
print(e)
f = e("CHF", "EUR")
print(f(10)())
e2 = curry(exchange)
f = e2(to_currency="USD", amount=100, from_currency="CHF")
f()
#print(f(from_currency="CHF")())
```

```
def arimean(*args):
return sum(args) / len(args)
def curry(func):
f_args = []
f_kwargs = {}
def f(*args, **kwargs):
nonlocal f_args, f_kwargs
if args or kwargs:
f_args += args
f_kwargs.update(kwargs)
try:
return func(*f_args, **f_kwargs)
except TypeError:
return f
else:
return func(*f_args, **f_kwargs)
return f
s = curry(arimean)
s(2, 4, 5)
e = curry(exchange)
print(e)
f = e("CHF", "EUR")
print(f(10))
e2 = curry(exchange)
f = e2(to_currency="USD", amount=100)
f("CHF")
```

```
from functools import partial
f = partial(exchange,
to_currency="USD",
amount=100)
f("CHF")
f = partial(f, "CHF")
f()
```

Next Chapter: Finite State Machine in Python