Scala Closures
In Scala, a closure is a function that can capture and include free variables from its defining environment.
A closure is a function whose return value depends on one or more variables declared outside the function.
A closure can access variables outside the function and use them inside the function. Even when the scope of the variables has gone beyond its defining environment, the closure can still access these variables.
The key to a closure is that it can "close over" variables outside the function, making these variables still available within the function.
Like the following anonymous function:
val multiplier = (i:Int) => i * 10
Inside the function body there is a variable i, which is a parameter of the function. As in the following piece of code:
val multiplier = (i:Int) => i * factor
In multiplier there are two variables: i and factor. One of them, i, is a formal parameter of the function; when the multiplier function is called, i is assigned a new value. However, factor is not a formal parameter but a free variable. Consider the following code:
var factor = 3 val multiplier = (i:Int) => i * factor
Here we introduce a free variable factor, which is defined outside the function.
The function variable multiplier defined in this way becomes a "closure", because it references a variable defined outside the function. The process of defining this function captures this free variable to form a closed function.
Complete Example
Example
def main(args: Array[String]) {
println( "muliplier(1) value = " + multiplier(1) )
println( "muliplier(2) value = " + multiplier(2) )
}
var factor = 3
val multiplier = (i:Int) => i * factor
}
Run Example »
Executing the above code produces the following output:
$ scalac Test.scala $ scala Test muliplier(1) value = 3 muliplier(2) value = 6
In the following example, the closure function captures the external variable externalVar. When the value of externalVar changes, the calculation result inside the closure changes accordingly.
Example
def main(args: Array[String]): Unit = {
var externalVar = 10
val closure = (x: Int) => x + externalVar
println(closure(5)) // Output 15
externalVar = 20
println(closure(5)) // Output 25
}
}
Executing the above code produces the following output:
15 25
Closures as Return Values
In the following example, the makeAdder function returns a closure. Different closure instances capture different adder values.
Example
def main(args: Array[String]): Unit = {
def makeAdder(adder: Int): Int => Int = {
(x: Int) => x + adder
}
val addFive = makeAdder(5)
val addTen = makeAdder(10)
println(addFive(3)) // Output 8
println(addTen(3)) // Output 13
}
}
Executing the above code produces the following output:
8 13
Types of Variables Captured by Closures
Closures can capture variables of different types, including value types and reference types.
In the following example, the addMessage closure captures the messages variable and can modify it.
Example
def main(args: Array[String]): Unit = {
var messages = List("Hello", "World")
val addMessage = (msg: String) => messages = messages :+ msg
addMessage("Scala")
println(messages) // Output List(Hello, World, Scala)
}
}
Executing the above code produces the following output:
List(Hello, World, Scala)
Closures and Functional Programming
Closures are an important concept in functional programming and are widely used in techniques such as higher-order functions and currying.
In the following example, the highOrderFunction function accepts a function as a parameter. The passed closure captures the external variable externalVar.
Example
def main(args: Array[String]): Unit = {
def highOrderFunction(f: Int => Int, x: Int): Int = f(x)
val externalVar = 5
val closure = (x: Int) => x + externalVar
println(highOrderFunction(closure, 10)) // Output 15
}
}
Executing the above code produces the following output:
15Other Extensions