What is a 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 in Closure and a Function.
Let's try to understand it with a simple example.
The above code is a function definition. It takes your salary as input. Then it calculates and returns your hike.
Free Variable
Have you noticed the
p in the above code? What is that?
That is the percentage of the hike. Where is the definition of
p? I mean, we are not passing
p as a parameter. We have not defined
p as a local value. The value
p does not have any definition or meaning within the function. So, what is
the
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 is the only difference between a function and
a
Closure. A closures uses one or more free variables. If you try to compile the
above
function, You will get an error because the compiler cannot find the
p.
So we still have one pending question. Where will the compiler find the p? The answer is straightforward. The Scala compiler looks into 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 try compiling the function once again.
It worked. So, the compiler detects that the function does not 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 does not complain.
More on Closures
There are few more questions to answer about the Closures.
- What if the value of a free variable changes?
- What if the closure modifies the value of a free variable?
- Why Closure? What are the benefits of a Closure?
What if the value of a free variable changes?
When you execute a closure, it takes the most recent state of the free variables. Let me show you that.
The first line defines the value of p as 10. The second line defines the closure. Then, I call the closure. It gives me a 10% of my salary. Then I change the value of p to 20 and call the closure once again. I 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 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.
The first line of the above code defines p as 10. We call the Closure and check the value of the p once again. It becomes 20. So the changes made inside the closure are passed back as well.
Why closure? What are the benefits of a Closure?
We learned that in functional programming, you could pass functions and return
functions.
Don't you think that it is similar to OOP?
In the OOP approach, we pass 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.
They carry methods as well as the data elements. Whereas, a function is alone
because
It does not have any state or a data item. 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. 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 shown below.
I want a function that I can apply to all of these ids and get the hike for all of them. It should work like the second line of code shown above. Here is the code for the getHike function.
There are many things in the code, but I want you to focus on the last line. The last line is an anonymous function body. The getHike is a Higher Order function because it returns another function. The below code demonstrates the main idea.
The first line makes a call to getHike that returns a function. The
returned
function takes an integer and gives a tuple of an integer and double.
In the next line, we call the function f with an employee number. It
will
give you the hike.
Further, In the next line, we pass an employee number that doesn't exist and
get
an exception. Now, I have a question.
Where is the f looking for employee number?
Look at the code for getHike. We have e and p>
defined
there. They are local values for the getHike function. 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.
Isn't
it?.
It is 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.
Read More
Pure Functions | Referential Transparency | Benefits of pure functions | First class functions | Higher order function | Anonymous functions | Immutability | Tail Recursion | Expressions in Scala | Lazy Evaluations | Pattern Matching | Closures