Closures in Scala

Closures basically means a function dependent on a free variable. A closure allows a function to access variables outside its immediate lexical scope.

Before we begin discussing closures in Scala, let us have a quick look at a function. The function literal billbelow accepts only one input parameter: netBill. However, the function calculates the final bill by adding a service charge to netBill.

val bill = (netBill: Double) => netBill*(1 + serviceCharge/100)

So if we try to compile the above function, Scala is going to complain that it does not know anything about serviceCharge.

We have one bounded variablenetBill. It is bound to the function call and takes its value from whatever is provided to the function. But serviceCharge is a free variable and the function literal does not provide it with any value.

In order to execute our function literal, we need to provide a value to serviceCharge outside of the function bill.

val serviceCharge = 14

The function value created at runtime from this function literal bill is called a closure because we capture the binding of its free variable by closing the function. In the above example, we have captured the value of serviceCharge as 14.

Complete Program:

object ClosureExample {
  def main(args: Array[String]): Unit = {
    val serviceCharge = 14;
    val bill = (netBill: Double) => netBill + serviceCharge
    println(bill(200))
    println(bill(100))
  }
}
val knownBill = (netBill: Double) => netBill * (1 + 12/100);

Here, knownBill is not a closure in true sense, because it is not dependent on any free variable.

Explaining Closure with Problem statement and its real use:

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:

package userDefinedPackage {
  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 {
  def main(args: Array[String]): Unit = {
    val foo = new userDefinedPackage.Foo
    var greetings = "Hello"
    val sayHello = (name: String) => println(greetings + " " + name)
    foo.exec(sayHello, "Harshit") // Hello Harshit
    greetings = "Hi"
    foo.exec(sayHello, "Harshit") // Hi Harshit
  }
}

From above program, we can say that A function defined inside another function has access to all the variables declared in the outer function.

The inner function will continue to have access to the variables from the outer scope even after the outer function has returned and completed.

In above program, the outer function name is main method, inner function name is sayHello and outer function has variable greetings which is accessible by inner function sayHello even after main method has returned.

Not only did the sayHello method reference the variable greetingsfrom within the exec method of the Foo class on the first run (where greetings was no longer in scope), but on the second run, it also picked up the change to the greetings (from Hello to Hi). The simple answer is that Scala supports closure functionality, and this is how closures work.

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:

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

Complete Program:

package votePackage {
  class Vote {
    def votingAllowed(f: Int => Boolean, votingAgeCriteria: Int) = {
      f(votingAgeCriteria)
    }
  }
}

object ClosureExample {
  def main(args: Array[String]): Unit = {
    val vote = new votePackage.Vote
    val votingAgeCriteria = 18;
    val isOfVotingAge = (age: Int) => age >= votingAgeCriteria
    println(vote.votingAllowed(isOfVotingAge, 21)) // True
    println(vote.votingAllowed(isOfVotingAge, 11)) // False
  }
}

Using closures with other data types:

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)
}

Now, pass addToBasket and a String parameter to the butStuff method:

buyStuff(addToBasket, "cherries") // apple, cherries

buyStuff(addToBasket, "grapes") // apple, cherries, grapes

Complete Program:

package fruitsPackage {
  class Fruits {
    def buyStuff(f: String => Unit, fruit: String) = {
      f(fruit)
    }
  }
}

import scala.collection.mutable.ArrayBuffer

object ClosureExample {
  def main(args: Array[String]): Unit = {
    val fruitArrayBuffer = ArrayBuffer("apple")
    val addToBasket = (fruit: String) => {
      fruitArrayBuffer += fruit
      println(fruitArrayBuffer.mkString(","))
    }
    val fruits = new fruitsPackage.Fruits
    fruits.buyStuff(addToBasket, "mango") // apple,mango
    fruits.buyStuff(addToBasket, "orange") // apple,mango,orange
  }
}

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.