Currying is a technique in functional programming to transform a function with multiple arguments into a sequence of nested functions with fewer arguments.

The number of arguments to a function is also called **arity**.

In other words, currying is a process to reduce the arity of some callable `f(a, b, c)`

, by translating it to a sequence of callables as `f(a)(b)(c)`

.

As an example, we can translate the below function with 3 arguments

```
# function
def add(a, b, c):
return a+b+c
# fn call
add(1, 2, 3) # 6
```

to a nested sequence of 3 functions (with one argument each):

```
# curried function
def add(a):
def _n1(b):
def _n2(c):
return a+b+c
return _n2
return _n1
#fn call
add(1)(2)(3) # 6
```

Let's unravel `add(1)(2)(3)`

to understand it better:

```
_obj1 = add(1)
_obj2 = _obj1(2)
result = _obj2(3)
print(result) # 6
```

Here, `_obj1`

holds the function returned by `add`

(`return _n1`

):

```
# _obj1
def _n1(b):
def _n2(c):
return a+b+c
return _n2
```

`_obj2`

holds the function returned by `_obj1`

(`return _n2`

):

```
def _n2(c):
return a+b+c
```

When `_obj2`

is called with the third argument (3), it calculates the result using its parameters (`c = 3`

) and the parameters passed to its parent functions (`a = 1 & b = 2`

).

How does it access these outer parameters? Being a nested function, `_n2`

has access to the variable scope of the outer functions `_n1`

and `add`

.

```
...
def _n2(c):
print(locals()) # {'c': 3, 'a': 1, 'b': 2}
...
...
```

Using currying, we can get partially evaluated functions. As in, we can store intermediate results of partial functions. This provides many benefits like interim analysis, saving checkpoint states, "resume" capability, etc.

```
def add(x, y):
return x+y
# partial function
add_7 = lambda x: add(x, 7)
add_7(3) # 10
```