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:
Ignore currying... to start with, it doesn't matter how F# enables partial application
Just remember that there is a capability through which you can call a function without all the parameters
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:
Partial application is available only from left to right, that is, there is no provision to pass some parameters from the right side
The order of parameters is important when you are designing/writing functions
Moving from left to right, you should have parameters in the increasing order of specialized-ness
This is a use-case, purpose-dependent issue;
If you have reached so far, congratulations.
Keep reading!