Scala Foundation Course - Scala Closures


What is a Scala closure?

A closure is a function. Like any other Scala function, a closure may be pure or impure. It may be named or anonymous, but it is primarily a function. You might be wondering then why we call it a Closure. There is a small difference. Let me explain.

                                    
    def getHike(salary:Double) = salary * p/100                                         
                             

Let's look at the above function definition. It takes your salary as input. Then it calculates and returns your hike. Right?
Have you noticed that p? What is that? That's the percentage of the hike. But from where is it coming? I mean, we are not passing p as a parameter. We haven't defined p as a local value. The p doesn't have any definition or meaning within the function.
So, what is p?
The p is a free variable for this function.
A Function that uses one or more free variables is known as a closure. That's the only difference between a Function and a Closure.
A closure uses one or more free variables. If you try to compile the above function, you willget an error as compiler can't find p.
So, we still have one pending question. Where will the compiler find the p?
The answer is straightforward. The Scala compiler investigate the so-called nearest local lexical environment, in which that function was defined and tries to find a binding. So, let me define p outside the function and compile the function once gain.


                                    
    val p =10
    def getHike(salary:Double) = salary * p/100                                     
                             

If you test it, it will work. The compiler detects that the function doesn't have a local definition for p and it is not even part of the parameter list. So, the variable p is a free variable, and the function is a Closure. Then it starts looking for the p. It starts searching for p in the lexical environment. Since the compiler finds the p, it doesn't complain.
Good, I guess you understand the closures. But that's not all about closure. There are few more questions to answer.

  1. What if the value of p changes?
  2. What if the closure modifies the value of a free variable?
  3. Why closure? What are the benefits?

Let me take these questions one by one.

What if the value of free variable changes?

When you execute a closure, it takes the most recent state of the free variables. Let me show you that.

                                        
    //I defined the value of p as 10.
    var p =10
    //Then I define this closure.
    def getHike(salary:Double) = salary * p/100                                     
                                 

Now, If I call this closure, it should give me a 10% of my salary. Right?
If you change value of p to 20 and call the closure once again. You get 20% hike. So, the closure takes the latest state of the free variable. Let me ask you another question.
Is this closure pure?
The answer is No because the free variable p is a var. If the free variable is a val, the closure is pure because we know that the val is a constant. It will never change, and hence the function will give the same value for the same input parameter. In fact, the value directly depends on the parameters, and the closure becomes referentially transparent.
So, a Closure may be pure or impure depending on the type of the free variable.

What if the closure modifies the value of a free variable?

If a closure modifies the free variable, the changes are visible outside the closure.

                                    
    var p =10
    def getHike(salary:Double) = { 
        p=p*2
        salary * p/100
    }
    println(p)
    //So, p is 10. We call the Closure. Then we check p once again.
    getHike(5000)
    println(p)                                      
                             

It becomes 20. So, the changes made inside the closure are passed back as well.

Why do we need Closure?

We learned that in functional programming, you could pass functions and return functions. Right? Isn't it the similar thing as OOP? In the OOP approach, we move objects around, and in functional programming, we juggle with functions. Both are the same kind of things. Isn't it?
If you agree with this, you can easily argue that objects are more flexible than functions. Why?
For certain use cases, objects are more flexible because objects carry two things. Methods and data elements. Whereas, a function is alone because It doesn't have any state or a data item. Right?
So, if we have a requirement, where we need to pass a bunch of states along with a function, how will you do it? The answer to this problem is a closure and the free variables.
Now, since you understand the purpose of a closure, let's look at an example to see it in action. We looked at the getHike function. Let me give you a realistic requirement. I have a list of employee numbers. It is something like this.

                                    
    val l = (1001 to 1005).toList                                           
                             

I want a function that I can apply to all these IDs and get the hike for all of them.It should work like this.

                                        
    l.map(getHike)                                           
                                 

Let's look at the code for my getHike function.

                                    
    def getHike = (empID:Int) => {
        //Load employee and their current salary
        val e:Map[Int,Double] = Map(1001->35000.00, 
                                    1002->43000.00, 
                                    1003->28000.00, 
                                    1004->54000.00, 
                                    1005->17000.00)
        // Some logic to derive percentage for each employee
        val p:Map[Int,Double]  = Map(1001 -> 10.00, 
                                    1002->12.00, 
                                    1003->7.50, 
                                    1004->6.80, 
                                    1005->20.00)
        (empID, e(empID) * p(empID) /100.00) 
    }                                       
                             

Little complicated to understand because we haven't learned Scala language yet. But, I want you to focus on the last line. The last line is an anonymous function body. The getHike is a HO function because it returns another function. Let me show you.

                                    
    val f = getHike
    //Output
    f: Int => (Int, Double) = <function1>                                       
                             

Did you notice the return type? The variable f is holding an anonymous function. The returned function takes an integer and gives a tuple of an integer and double. If we call this function with an employee number, it will give you the hike.

                                        
    f(1001)
    //Output
    res4: (Int, Double) = (1001,3500.0)                                     
                                 

If you pass an employee number that doesn't exist, you get an exception.

                                            
    f(1006)
    //NoSuchElement                                     
                                     

And here is my question to you. Where is this f looking for employee number? Think about it.
Look at the code for getHike. We have e and p defined inside the function body. They are local values for the getHike function. Right? But they are free variables for the anonymous function body in the last line.
Now, here is the point that I want to make.
The anonymous function returned from the last line is a closure. It uses two free variables. e and p. When we return it from the getHike, it carries the state of the e and the p with it. So, f contains the data with it. Right? Just like an object in the object-oriented world. When you start practicing functional programming, you will realize that closures and their capability to carry the state is incredible. It saves you a lot of complicated and unnecessary code and simplifies your solution.
Thank you for watching Learning Journal. Keep learning and Keep growing.


You will also like:


Functional Programming

What is Functional Programming and why it is important?

Learning Journal

Pure Functions

What are pure functions and side effects. Start learning functional programming.

Learning Journal

Statements and Expressions

Statements and Expressions in Scala. How are they different?

Learning Journal

Scala named arguments

Learn about named arguments and default values in Scala functions with examples.

Learning Journal

Anonymous Functions

Learn Scala Anonymous Functions with suitable examples.

Learning Journal