Idea of 'closure' in functional programming
Closure
- The inner function has access to variables which were in the enclosing scope even after that ’ennclosing scope’ execution is over. (Inner function gets a copy) This is my attempt to understand why is this such an important concept in functional programming, why is it named so and so on…
Functions are first class citizens
In functional paradigm of programming functions are considered as ‘first class citizens’. This means functions can be
- assigned to a variable
- passed as argument to another function
- returned from another function
- functional composition is possible (typical: gof(x), fog(x))
What happens when we create/assign values to variable?
It allocates some memory, stores some values in that address and we have that information in our variable.
If we take this thinking towards our ‘function variable’, what should it have?
Of course it will hold the ‘function code to execute’. But should it also have some ‘context’ of it’s own when it was invoked. (Remember we are storing this in a variable mostly for later use or re-use)
This is where closure comes into picture. It gives function that ‘missing context’ upon which we can evaluate.
Why the name closure?
The part of the environment which gives the free variables in an expression their meanings, is the closure. It is called this way, because it turns an open expression into a closed one, by supplying these missing definitions for all of its free variables, so that we could evaluate it.
Here is one such example in go-lang
package main
import (
"fmt"
)
func main(){
greet_ram:= greet_invoker("ram")
greet_ram()
greet_ram()
greet_sita:= greet_invoker("sita")
greet_sita()
greet_sita()
}
func greet_invoker(name string) func(){
count:=0
return func(){
count++
fmt.Printf("Hello %s for %d th time\n",name, count)
}
}
Output
Hello ram for 1 th time
Hello ram for 2 th time
Hello sita for 1 th time
Hello sita for 2 th time
In this example, greet_ram and greet_sita are not just pointers to where the function is but they have the whole contextual information about that function so that you can invoke it later as well building on the context.
Example in javascript
Following is the same example in javascript.
var greet_invoker = function(name){
var greet_count=0
return function greet(){
console.log(greet_count++)
console.log(name)
}
}
undefined
ram_greet = greet_invoker("ram")
ƒ greet(){
console.log(greet_count++)
console.log(name)
}
ram_greet()
VM563:4 0
VM563:5 ram
undefined
ram_greet()
VM563:4 1
VM563:5 ram
undefined
ram_greet()
VM563:4 2
VM563:5 ram
undefined
sita_greet = greet_invoker("sita")
ƒ greet(){
console.log(greet_count++)
console.log(name)
}
sita_greet()
VM563:4 0
VM563:5 sita
undefined
sita_greet()
VM563:4 1
VM563:5 sita
undefined
ram_greet()
VM563:4 3
VM563:5 ram
undefined
sita_greet()
VM563:4 2
VM563:5 sita
undefined
References/Shoutouts: