The Power of Partial Application
by Scott Wlaschin
FP Bridge Consulting
Some functional programming principles can seem bizarre and hard to understand when you first encounter them, but are immensely powerful when you master them. In this article we’ll look at the technique called “partial application” and see how it can be used in practice.
When a newcomer looks at F#, one of the first things that leaps out at them is that whitespace is used between the arguments in function calls rather than parentheses. For example, here is a simple “add” function:
// define a function let add x y = x + y // use the function let result = add 1 2
Why don’t functional languages like F# use parentheses, like “normal” languages? Why isn’t a function call written the standard way?
let result = add(1,2) // why not this way?
The reason is that, with functional languages, the parentheses can be put in a completely unexpected place; around the function name and first argument, like this:
let result = (add 1) 2
Furthermore, the code in parentheses can actually be extracted into a separate function altogether and then called later:
let partialFn = (add 1) let result = partialFn 2
The act of passing fewer parameters than needed is called “partial application”.
Behind the scenes, you could think of “add” as being implemented as a one parameter function that returns a lambda (another one parameter function). The “x” parameter is “baked into” the lambda, so that it in turn only needs to take a single “y” parameter.
// alternate definition let add x = (fun y -> x + y)
Any multi-parameter function can be treated this way. So for example, a three parameter “add” function can be treated as a two parameter function that returns a new one parameter function, like this:
// normal version: add three parameters let add3 x y z = x + y + z
// alternate definition let add3 x y = (fun z -> x + y + z)
This concept is very different from what you might be used to non-functional languages, and you might be thinking: What good is it? What possible use can it have?
Let me answer that by showing you three important examples of the power of partial application.
Partial application with maps and filters
In a functional programming language, when working with lists and other collections, you avoid “foreach” loops and tend to work on the entire collection at once, passing in a function that is applied to every element in the collection. The List.map function, for example, is defined like this:
List.map (function to apply to each element) (a list)
So to add 1 to each element of a list we might write something like this:
List.map (fun elem -> elem + 1) [1..10]
But that lambda is exactly the same as the output of the partially applied “add” function that we implemented earlier. In other words, we can reuse the “add” function by partially applying it with only one parameter, and passing the result to the map function, like this:
List.map (add 1) [1..10]
Similarly, we could create a general “isDivisibleBy” function
let isDivisibleBy divisor input = ( input % divisor = 0)
And then we can use it in a partially applied form too, with a filter function, like this:
List.filter (isDivisibleBy 2) [1..10]
This approach becomes even more useful when you need to chain a series of functions together, like this:
[1..10] |> List.filter (isDivisibleBy 2) |> List.map (add 20)
So, when you see functions used in this way, you are seeing partial application in action.
Share This Article:
The F# Gazette
Relax and enjoy a curated selection of the best F# content straight to your inbox.
- F# news round-up
- Interviews with industry professionals
- Guest articles from F# experts
- Latest F# jobs
- F# tools, tips, tutorials and more.