Skip to content

Scala Closures

A closure is a function that refers to variables defined outside its own body (known as free variables). The function ‘closes over’ these variables, meaning it retains access to them even when the function is executed in a different scope from where it was defined.

Consider a simple function literal:

val addTen = (x: Int) => x + 10

Here, the only variable used in the body x + 10 is x, which is a parameter to the function. This function is not a closure because it has no free variables.

Now, let’s introduce a free variable:

var factor = 5 // Variable defined outside the function
// This function literal uses 'factor', which is a free variable
val multiplier = (i: Int) => i * factor

The function multiplier is a closure because it depends on factor, which is defined in the enclosing scope. The closure captures the reference to factor, not just its value at the time of definition.

object ClosureDemo {
var factor = 3 // Free variable
val multiplier = (i: Int) => i * factor // Closure capturing 'factor'
def main(args: Array[String]): Unit = { // Or use @main
println(s"multiplier(1) value = ${multiplier(1)}") // Uses factor = 3
println(s"multiplier(2) value = ${multiplier(2)}") // Uses factor = 3
// Change the free variable
factor = 10
println("--- Factor changed to 10 ---")
println(s"multiplier(1) value = ${multiplier(1)}") // Uses factor = 10
println(s"multiplier(2) value = ${multiplier(2)}") // Uses factor = 10
}
}

Output:

multiplier(1) value = 3
multiplier(2) value = 6
--- Factor changed to 10 ---
multiplier(1) value = 10
multiplier(2) value = 20

As the output shows, the closure multiplier always reads the current value of the captured variable factor each time it’s called. If the captured variable is mutable (var), changes to it are reflected in subsequent calls to the closure.

Closures can also capture immutable variables (val). In this case, the captured value will never change.

val fixedIncrement = 5
val addFixed = (x: Int) => x + fixedIncrement // Captures immutable 'fixedIncrement'
@main def ImmutableClosureDemo(): Unit = {
println(addFixed(10)) // Output: 15
// fixedIncrement = 10 // This would be a compile error as 'fixedIncrement' is a val
}

Closures are fundamental to functional programming and are used extensively in Scala:

  • Event Handlers/Callbacks: A handler function often needs access to variables from the scope where it was defined (e.g., UI elements, state variables).
  • Asynchronous Programming: Callbacks passed to asynchronous operations (like Future or IO) often capture context needed when the operation completes.
  • Building DSLs: Closures can create functions with context-specific behavior.
  • Partial Application & Currying: These techniques often result in closures.

Understanding closures is essential for mastering functional programming in Scala and leveraging libraries like Akka, Cats, or ZIO.