This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 9.5, “How to use closures in Scala (closure examples, syntax).”
Problem
You want to pass a Scala function around like a variable, and while doing so, you want that function to be able to refer to one or more fields that were in the same scope as the function when it was declared.
Solution
You can demonstrate a closure in Scala with the following simple (but complete) example:
package otherscope { class Foo { // a method that takes a function and a string, and passes the string into // the function, and then executes the function def exec(f:(String) => Unit, name: String) { f(name) } } } object ClosureExample extends App { var hello = "Hello" def sayHello(name: String) { println(s"$hello, $name") } // execute sayHello from the exec method foo val foo = new otherscope.Foo foo.exec(sayHello, "Al") // change the local variable 'hello', then execute sayHello from // the exec method of foo, and see what happens hello = "Hola" foo.exec(sayHello, "Lorenzo") }
To test this code, save it as a file named ClosureExample.scala, then compile and run it. When it’s run, the output will be:
Hello, Al Hola, Lorenzo
If you’re coming to Scala from Java or another OOP language, you might be asking, “How could this possibly work?”
Not only did the sayHello
method reference the variable hello
from within the exec
method of the Foo
class on the first run (where hello
was no longer in scope), but on the second run, it also picked up the change to the hello
variable (from Hello
to Hola
). The simple answer is that Scala supports closure functionality, and this is how closures work.
As Dean Wampler and Alex Payne describe in their book Programming Scala (O’Reilly), there are two free variables in the
sayHello
method: name andhello
. Thename
variable is a formal parameter to the function; this is something you’re used to.However,
hello
is not a formal parameter; it’s a reference to a variable in the enclosing scope (similar to the way a method in a Java class can refer to a field in the same class). Therefore, the Scala compiler creates a closure that encompasses (or “closes over”)hello
.
You could continue to pass the sayHello
method around so it gets farther and farther away from the scope of the hello
variable, but in an effort to keep this example simple, it’s only passed to one method in a class in a different package. You can verify that hello
is not in scope in the Foo
class by attempting to print its value in that class or in its exec
method, such as with println(hello)
. You’ll find that the code won’t compile because hello
is not in scope there.
Discussion
In my research, I’ve found many descriptions of closures, each with slightly different terminology. Wikipedia defines a closure like this:
“In computer science, a closure (also lexical closure or function closure) is a function together with a referencing environment for the non-local variables of that function. A closure allows a function to access variables outside its immediate lexical scope.”
In his excellent article, Closures in Ruby, Paul Cantrell states, “a closure is a block of code which meets three criteria.” He defines the criteria as follows:
- The block of code can be passed around as a value, and
- It can be executed on demand by anyone who has that value, at which time
- It can refer to variables from the context in which it was created (i.e. it is closed with respect to variable access, in the mathematical sense of the word “closed”).
Personally, I like to think of a closure as being like quantum entanglement, which Einstein referred to as “a spooky action at a distance.” Just as quantum entanglement begins with two elements that are together and then separated — but somehow remain aware of each other — a closure begins with a function and a variable defined in the same scope, which are then separated from each other. When the function is executed at some other point in space (scope) and time, it is magically still aware of the variable it referenced in their earlier time together, and even picks up any changes to that variable.
As shown in the Solution, to create a closure in Scala, just define a function that refers to a variable that’s in the same scope as its declaration. That function can be used later, even when the variable is no longer in the function’s current scope, such as when the function is passed to another class, method, or function.
Any time you run into a situation where you’re passing around a function, and wish that function could refer to a variable like this, a closure can be a solution. The variable can be a collection, an Int
you use as a counter or limit, or anything else that helps to solve a problem. The value you refer to can be a val
, or as shown in the example, a var
.
A second example
If you’re new to closures, another example may help demonstrate them. First, start with a simple function named isOfVotingAge
. This function tests to see if the age
given to the function is greater than or equal to 18
:
val isOfVotingAge = (age: Int) => age >= 18 isOfVotingAge(16) // false isOfVotingAge(20) // true
Next, to make your function more flexible, instead of hardcoding the value 18
into the function, you can take advantage of this closure technique, and let the function refer to the variable votingAge
that’s in scope when you define the function:
var votingAge = 18 val isOfVotingAge = (age: Int) => age >= votingAge
When called, isOfVotingAge
works as before:
isOfVotingAge(16) // false isOfVotingAge(20) // true
You can now pass isOfVotingAge
around to other methods and functions:
def printResult(f: Int => Boolean, x: Int) { println(f(x)) } printResult(isOfVotingAge, 20) // true
Because you defined votingAge
as a var
, you can reassign it. How does this affect printResult
? Let’s see:
// change votingAge in one scope votingAge = 21 // the change to votingAge affects the result printResult(isOfVotingAge, 20) // now false
Cool. The field and function are still entangled.
Using closures with other data types
In the two examples shown so far, you’ve worked with simple String
and Int
fields, but closures can work with any data type, including collections. For instance, in the following example, the function named addToBasket
is defined in the same scope as an ArrayBuffer
named fruits
:
import scala.collection.mutable.ArrayBuffer val fruits = ArrayBuffer("apple") // the function addToBasket has a reference to fruits val addToBasket = (s: String) => { fruits += s println(fruits.mkString(", ")) }
As with the previous example, the addToBasket
function can now be passed around as desired, and will always have a reference to the fruits
field. To demonstrate this, define a method that accepts a function with addToBasket
’s signature:
def buyStuff(f: String => Unit, s: String) = { f(s) }
Then pass addToBasket
and a String parameter to the method:
scala> buyStuff(addToBasket, "cherries") apple, cherries scala> buyStuff(addToBasket, "grapes") apple, cherries, grapes
As desired, the elements are added to your ArrayBuffer
.
Note that the buyStuff
method would typically be in another class, but this example demonstrates the basic idea.
A comparison to Java
If you’re coming to Scala from Java, or an OOP background in general, it may help to see a comparison between this closure technique and what you can currently do in Java. (In Java, there are some closure-like things you can do with inner classes, and closures are intended for addition to Java 8 in Project Lambda. But this example attempts to show a simple OOP example.)
The following example shows how a sayHello
method and the helloPhrase
string are encapsulated in the class Greeter
. In the main
method, the first two examples with Al
and Lorenzo
show how the sayHello
method can be called directly.
At the end of the main
method, the greeter
instance is passed to an instance of the Bar
class, and greeter
’s sayHello
method is executed from there:
public class SimulatedClosure { public static void main (String[] args) { Greeter greeter = new Greeter(); greeter.setHelloPhrase("Hello"); greeter.sayHello("Al"); // "Hello, Al" greeter.setHelloPhrase("Hola"); greeter.sayHello("Lorenzo"); // "Hola, Lorenzo" greeter.setHelloPhrase("Yo"); Bar bar = new Bar(greeter); // pass the greeter instance to a new Bar bar.sayHello("Adrian"); // invoke greeter.sayHello via Bar } } class Greeter { private String helloPhrase; public void setHelloPhrase(String helloPhrase) { this.helloPhrase = helloPhrase; } public void sayHello(String name) { System.out.println(helloPhrase + ", " + name); } } class Bar { private Greeter greeter; public Bar (Greeter greeter) { this.greeter = greeter; } public void sayHello(String name) { greeter.sayHello(name); } }
Running this code prints the following output:
Hello, Al Hola, Lorenzo Yo, Adrian
The end result is similar to the Scala closure approach, but the big differences in this example are that you’re passing around a Greeter
instance (instead of a function), and sayHello
and the helloPhrase
are encapsulated in the Greeter
class. In the Scala closure solution, you passed around a function that was coupled with a field from another scope.
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
See Also
- The voting age example in this recipe was inspired by Mario Gleichmann’s example in Functional Scala: Closures
- Paul Cantrell’s article, Closures in Ruby
- Recipe 3.18, “Creating Your Own Scala Control Structures”, demonstrates the use of multiple parameter lists
- Java 8’s Project Lambda