Curried functions in Python
Tags: python, functional
Published: 2020-04-23 22:15:55

Let's create curried functions in Python!

You can search for curried functions on the internet and you will find very scary things [Wiki](https://en.wikipedia.org/wiki/Currying). Some languages like Haskell have this functionality defined at the core and its something everyone plays on his favorite language when they enter the functional world.

Let's define what we want, not why we want it, because that's not important, right? We all want curried functions!

## Curried function

A curried function is a function that only receives one argument.

```python
def add2(x):
    return x + 2
```

But, what if we want to create a function that receives more than one argument? Well, curried functions can return any value, including another function, preferably a curried one.

```python
def add(x):
    def add_x(y):
        return x + y
    return add_x
```

But, what if we want to add even more arguments? It gets pretty ugly when the number of arguments that our function needs increases.

Let's try to deal with this with a more general implementation.

How do we use this function? Now the syntax for a function call is a little bit different and it can be strange. On the other hand, we now can get one "partially" evaluated function.

```python
add(2)(3)  # 5
add2 = add(2)  # function
add2(3)  # 5
```

This comes handy when we use `map` or `filter`

```python
def greater_than(x):
    def greater_than_x(y):
        return y > x
    return greater_than_x

list(
    map(
        add(2),
        filter(
            greater_than(5),
            range(10)
        )
    )
)  # [8, 9, 10, 11]
```

Ideally, we want to keep the nice syntax for function calls that Python has when we don't want to use the curried possibility.

## Proposal

Let's define a decorator that takes a normal python function and returns a curried version of it. We will define our function as usual and we will get both ways of calling it.

```python
@curried
def add(x, y):
    return x + y

add2 = add(2)  # function
add2(3)  # 5
add(2, 3)  # 5
```

This is a possible implementation of this `curried` decorator.

```python
from inspect import signature, Parameter


def curried(func):
    sig = signature(func)
    params = sig.parameters.values()

    assert all(map(is_positional, params)), \
        'All parameters should be positional'

    def curried_func_first_call(*args):
        N = len(params)
        applied = list(args)

        def curried_func(*args):
            assert len(applied) + len(args) <= N, \
                'Too many arguments, expected {}'.format(N)

            applied.extend(args)

            if len(applied) == N:
                return func(*applied)

            return curried_func

        if len(applied) == N:
            return func(*applied)

        return curried_func

    return curried_func_first_call


def is_positional(param):
    return param.kind == Parameter.POSITIONAL_ONLY or \
        param.kind == Parameter.POSITIONAL_OR_KEYWORD
```

## What about `partial`

Probably everyone has heard about `partial` function from the module `functools`. But this does not behave entirely as we want. It does a lot more than we are doing here, but I was interested in syntax.

One of the problems is that `partial` always returns a function, even when all the arguments were supplied.

```python
def add(x, y):
    x + y

f = partial(add, 2, 3)  # function
f()  # 5
```

Furthermore, `partial` does not curry the resulting function

```python
def add(x, y, z):
    return x + y + z

f = partial(add, 1)

f(2)  # TypeError: add() missing 1 required positional argument: 'z'
f(2, 3)  # 6
```