📜 ⬆️ ⬇️

Currying and partial application of the function

When I first heard the term Currying , I immediately imagined delicious Thai and Indian cuisines. To my surprise, I found that the conversation was not about fine spices, but about converting a function that takes n arguments to a function that takes one argument and returns a curried function that takes n - 1 arguments. Where could this be useful?

From a theoretical point of view, this is interesting, because in lambda calculus it is easier to operate with functions that have one argument. On the practical side, this gives the programmer the opportunity to create a family of functions from the base function by fixing the first k arguments. This is similar to how we fasten something to the wall and we need two studs for this. Until we stick a single stud, the object can freely move anywhere on the surface; however, movement is limited as soon as we pin it with one pin. And finally, when the second pin is stuck into this object, the object no longer has freedom of movement. Similarly, when a programmer curries a function from two arguments and applies it to the first argument, the functionality is limited to one dimension. Finally, when it applies a new function to the second argument, the final value is calculated.

In C #, this means that if I have a delegate of type Func <A, B, R> (a delegate with two arguments of types A and B respectively and returning type R), then I can create a delegate that has type Func <A, Func <B, R >>. Note that the curried delegate takes only one argument, but returns a delegate that takes the second argument of the original function and ultimately returns the value.

Consider the creation of such functions on the example of the addition function.
')
Func < int , int , int > add = (x,y) => x + y;

We can curry addition by applying the curry function to add.
Func < int , Func < int , int >>curriedAdd = add.Curry();

In fact, this curried function add creates functions that add to n, where n is the argument of the curried function add. For example, we can create an increment function by applying the curried function add to the value 1.

Func < int , int > inc = curriedAdd(1);

The incrementing function when called now returns one plus the value of its argument. Now we can use our functions for different forms of addition.

Console .WriteLine(add(3,4)); // 7 <br>
Console .WriteLine(curriedAdd(3)(5)); // 8 <br>
Console .WriteLine(inc(2)); // 3

So what does this Curry function look like? In fact, it is very simple.

public static Func <A, Func <B, R>> Curry<A, B, R>( this Func <A, B, R> f)<br/>
{<br/>
return a => b => f(a, b);<br/>
}


It simply takes a function consisting of two arguments and returns a lambda that captures the first argument and then the second argument. Once both arguments are provided, it calls the original function with arguments. It is very easy to follow this pattern and create a Curry function that curries functions with a different arity.

Let's look at what happens when we create each of these functions. First, we created an add function that looks like this:
(x, y) => x + y
After we have curried add, the function takes the form:
x => y => x + y
We created inc by invoking a curried add with a value of 1. This essentially creates the following function:
y => 1 + y

The idea of ​​currying a function and then fixing the first n arguments of the original function can be generalized to a concept called partial application of the function. For example, if we take our add function from the previous example, we can directly create an increment from add without creating curriedAdd before this.

Func < int , int > inc = add.Partial(1);

Where the Partial function is written as:

public static Func <B, R> Partial<A, B, R>(this
Func <A, B, R> f, A a)<br>
{<br>
return b => f(a, b);<br>
}

Notice how the function accepts the function and the value a, which is of the same type as the first argument of the function. It returns a function that takes the remaining arguments and then applies all the arguments to the original function. This can be summarized into a set of functions that produce partially applied functions.

Source: https://habr.com/ru/post/76545/


All Articles