Basics 05 - Currying and Partial Application

Currying is what enables the Partial Application of functions.

Learning-FSharp/Ch05-CurryingAndPartialApplication/Program.fs

Check out all the comments that are placed in the source file.

Let's start with Currying.

Currying

Currying is what the F# compiler does for you: rewrites a multi-parameter function into a series of functions, all with just one parameter. Consider the following function signature:

TParam1 -> TParam2 -> TReturn

A function with 2 input parameters of Type1 and Type2, and the return value of TReturn. During compilation, the F# compiler rewrites this into:

TParam1 -> (TParam2 -> TReturn)

A function with 1 input parameter of Type1, which returns a function. The returned function has the signature TParam2 -> TReturn.

Assume this function is namedF and you call it with values v1 and v2:

let result = F v1 v2

Since F is compiled as a single parameter function, you may read the above as:

let result = (F v1) v2

That is, call F with v1, and call the returned function with v2.

Similarly, if you have a function name G with three parameters:

let result = G v1 v2 v3 can be read as:

let result = ((G v1) v2) v3

If you are wondering what's the need for this? Breaking down a multi-parameter function into a series of functions? The answer is to enable the partial application.

Partial Application

Partial application of a function, as opposed to the application of a function, is when you call a function without all the parameters. As a result of the partial application, what you get is a new function that accepts the remaining parameters.

Assume a function: let add3Nums x y z = x + y + z; signature: int -> int -> int -> int

A typical case: let total = add3Nums 1 2 3

The variable total is the sum of 1, 2 and 3

Partial application with 1 parameter: let partialFunc = add3Nums 1

The variable partialFunc is a function with signature int -> int -> int

let total = partialFunc 2 3

The variable total is the sum of 1, 2 and 3

Partial application with 2 parameters: let partialFunc = add3Nums 1 2

The variable partialFunc is a function with signature int -> int

let total = partialFunc 3

The variable total is the sum of 1, 2 and 3

Why do we need Partial Application?

To generate specialized versions of generalized functions. Simple.

(+) is a function in F#. A general function for adding numbers.

let result = 2 + 3

The above is the same as:

let result = (+) 2 3

The partial application allows you to take (+), which is a general-purpose function and do something like this:

let twoAdder = (+) 2

Here you have twoAdder, which is a specialized function. Whatever you pass to twoAdder, the result will be input + 2.

Here's a better example:

let printDateAndTime (format: string) (dateTimeFormat: string) (dateTime: DateTime) =

printfn "%s" (String.Format(format, dateTime.ToString(dateTimeFormat)))

This is a very generalized function for printing date and time in the desired format. The first parameter is for the formatting of the message, the second parameter is for DateTime to string formatting, and the third parameter is the DateTime value.

let withFormat = printDateAndTime "Current date and time is: {0}."

This creates a new function with the first parameter to printDateAndTime already supplied, which is the message format.

let withDateFormat = withFormat "D"

This creates a new function with the first parameter to withFormat already supplied, which is the date to string format.

withDateFormat DateTime.Now

Here the message is printed with DateTime.Now.

Too Much and Confusing?

If you find currying and partial application too much and confusing:

  1. Ignore currying... to start with, it doesn't matter how F# enables partial application

  2. Just remember that there is a capability through which you can call a function without all the parameters

  3. The result of calling a function without all the parameters is another function

Partial Application and Order of Parameters

Here are a few more cents:

  1. Partial application is available only from left to right, that is, there is no provision to pass some parameters from the right side

  2. The order of parameters is important when you are designing/writing functions

    1. Moving from left to right, you should have parameters in the increasing order of specialized-ness

    2. This is a use-case, purpose-dependent issue;

If you have reached so far, congratulations.

Keep reading!