I will discuss how multivariate functions are typically worked with in functional programming. In particular, the notion of *currying* will be explored.

The *arity* of a function is the number of arguments that the function takes. Functions with no arguments (referred to as *nullary* functions) have an arity of 0. Single-variable functions are referred to as *unary* functions and have an arity of 1.

Most functions take more than one variable. Such functions are referred to as *multi-arity* functions.

In particular, functions which have an arity of 2 or 3 are often referred to as *binary* and *ternary* functions, respectively.

Let

$f : (X\times Y) \rightarrow Z$be a binary function.

Such a function takes a pair $(x,y)\in X\times Y$ as its argument and outputs an element $f(x,y) = z \in Z$.

Suppose I want to evaluate $f$ at the point $(x_0,y_0)$.

I could explicitly pass both arguments to $f$ and compute the result directly.

Functional programming approaches this from a slightly different point-of-view (for reasons which will be discussed shortly).

Rather than passing the pair $(x_0,y_0)$ to $f$ directly, one first passes only $x_0$ to $f$, returning $f(x_0,y)$. But $f(x_0,y)$ is itself a function from $Y \to Z$. Let $f(x_0,y) = h(y)$. One then passes $y_0$ as an argument to $h$ which results in $h(y_0) = f(x_0,y_0)$.

In this way the evaluation of the multivariate function $f$ at $(x_0,y_0$) is divided into two separate *univariate* evaluations.

If we let $Y\to Z$ represent the set of all functions from $Y\to Z$ then we can define

$f_{\text{curried}}: X \to (Y\to Z)$to be the *curry* of $f$.

Let $f(x,y) = x + y : (\mathbb{Z} \times \mathbb{Z}) \to \mathbb{Z}$.

Let $(x_0,y_0) = (1,1)$.

We want to evaluate

$f_{\text{curried}} : \mathbb{Z} \to (\mathbb{Z} \to \mathbb{Z})$at $(x_0,y_0)$.

First,

$f_{\text{curried}} (1) = 1 + y$Next,

$f_{\text{curried}}(1)(1) = 1 + 1 = 2$Note that in the first evaluation $x_0=1$ is the input and the function $h(y) = f_{\text{curried}}(1)$ is the output.

In the second step the function $h(y)$ is itself evaluated at $y_0=1$ resulting

So, that’s currying.

Let

$f : (X\times Y\times Z) \to N$be a ternary function, for some sets $X,Y,Z,N$.

We will iteratively curry this function.

$\begin{aligned} (X\times Y\times Z) \to N &= X \to \underbrace{\big((Y\times Z) \to N\big )}_{\text{two-variable function}} \\ &= X\to \big(Y\to (Z\to N)\big) \end{aligned}$One can iteratively apply the above argument for $n$ arguments. The right-associativity is generally implicitly understood so that the parentheses are normally omitted.

For each of the below examples we will consider a simple addition function function $f(x,y) = x + y$.

```
add :: (Int, Int) -> Int
add(x,y) = x + y
```

```
add :: Int -> Int -> Int
add x y = x + y
```

```
main :: IO ()
add :: Int -> Int -> Int
add x y = x + y
add_ = add 1
main = print (add_ 1) -- 2
```

Uncurried ⟶ curried:

```
main :: IO ()
add :: (Int, Int) -> Int
add(x, y) = x + y
addCurried = curry add
addCurried_ = addCurried 1
main = print (addCurried_ 1) -- 2
```

Curried ⟶ uncurried

```
main :: IO ()
addCurried :: Int -> Int -> Int
addCurried x y = x + y
add = uncurry addCurried
main = print (add(1,1)) -- 2
```

` val add = (x: Int, y: Int) => x + y`

` val addCurried: Int => Int => Int = x => y => x + y`

```
val add: Int => Int => Int = x => y => x + y
val add_ = add(1)(_)
println(add_(1)) // 2
```

Uncurried ⟶ curried:

```
val add = (x: Int, y: Int) => x + y
val addCurried = add.curried
println(addCurried(1)(1)) // 2
```

Curried ⟶ uncurried:

```
val addCurried: Int => Int => Int = x => y => x + y
val add = Function.uncurried(addCurried)
println(add(1, 1)) // 2
```

One often sees the notion of *partial function application* discussed alongside *currying*. While they may seem similar, in reality they are different.

Currying takes an $n$-ary function and transforms it into $n$ *unary* functions. Partial function application takes an $n$-ary function, evaluates it for some subset of the arguments (say $x$ of them) and returns an $(n-x)$-ary function.

```
val add = (a: Int, b: Int, c: Int) => a + b + c
val addPartial = add(1, _: Int, _: Int)
println(addPartial(1,1)) // 3
```

Note how partial application mapped a ternary function to a binary function, whereas the curry of `add`

would map an argument to a *unary* function to a *unary* function, as below.

```
val add: Int => Int => Int => Int = x => y => z => x + y + z
val addPart = add(1)
val addPart_ = addPart(1)
println(addPart_(1)) // 3
```

Now that we understand what currying is and what it is not, the question remains, why bother currying at all?

In short, currying makes it syntactically easier to work with *higher order functions*, which will be discussed in a future post.