This is an excerpt from the 1st Edtion of the Scala Cookbook (partially modified for the internet). This is Recipe 9.6, “How to use partially applied functions in Scala.”
Problem
You want to eliminate repetitively passing variables into a Scala function by (a) passing common variables into the function to (b) create a new function that is preloaded with those values, and then (c) use the new function, passing it only the unique variables it needs.
Solution
The classic example of a partially applied function begins with a simple sum
function:
val sum = (a: Int, b: Int, c: Int) => a + b + c
There’s nothing special about this sum
function, it’s just a normal function. But things get interesting when you supply two of the parameters when calling the function, but don’t provide the third parameter:
val f = sum(1, 2, _: Int)
Because you haven’t provided a value for the third parameter, the resulting variable f
is a partially applied function. You can see this in the REPL:
scala> val sum = (a: Int, b: Int, c: Int) => a + b + c sum: (Int, Int, Int) => Int = <function3> scala> val f = sum(1, 2, _: Int) f: Int => Int = <function1>
The result in the REPL shows that f
is a function that implements the function1
trait, meaning that it takes one argument. Looking at the rest of the signature, you see that it takes an Int
argument, and returns an Int
value.
When you give f
an Int
, such as the number 3
, you magically get the sum of the three numbers that have been passed into the two functions:
scala> f(3) res0: Int = 6
The first two numbers (1
and 2
) were passed into the original sum
function; that process created the new function named f
, which is a partially applied function; then, some time later in the code, the third number (3
) was passed into f
.
Discussion
In functional programming languages, when you call a function that has parameters, you are said to be applying the function to the parameters. When all the parameters are passed to the function — something you always do in Java — you have fully applied the function to all of the parameters. But when you give only a subset of the parameters to the function, the result of the expression is a partially applied function.
As demonstrated in the example, this partially applied function is a variable that you can pass around. This variable is called a function value, and when you later provide all the parameters needed to complete the function value, the original function is executed and a result is yielded.
This technique has many advantages, including the ability to make life easier for the consumers of a library you create. For instance, when working with HTML, you may want a function that adds a prefix and a suffix to an HTML snippet:
def wrap(prefix: String, html: String, suffix: String) = { prefix + html + suffix }
If at a certain point in your code, you know that you always want to add the same prefix and suffix to different HTML strings, you can apply those two parameters to the function, without applying the html
parameter:
val wrapWithDiv = wrap("<div>", _: String, "</div>")
Now you can call the new wrapWithDiv
function, just passing it the HTML you want to wrap:
scala> wrapWithDiv("<p>Hello, world</p>") res0: String = <div><p>Hello, world</p></div> scala> wrapWithDiv("<img src=\"/images/foo.png\" />") res1: String = <div><img src="/images/foo.png" /></div>
The wrapWithDiv
function is preloaded with the <div>
tags you applied, so it can be called with just one argument: the HTML you want to wrap.
As a nice benefit, you can still call the original wrap
function if you want:
wrap("<pre>", "val x = 1", "</pre>")
You can use partially applied functions to make programming easier by binding some arguments — typically some form of local arguments — and leaving the others to be filled in.
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |