Introduction
In the previous part of the series, we covered anonymous functions and in this section, we will look into closures
which are quite a cool concept for various things. Closures are basically a function that returns a function instead of a value, so basically we will leverage anonymous functions for creating closures.
Simple Closures
A simple closure can be constructed for understanding how we can use closures in golang. We will return a function from a function, that is a simple closure. So, in the below code example, we have created a function gophy()
which takes no parameters but returns a function that returns a string
. The function simply returns an anonymous function that returns a string.
We will initialize the variable g
that is assigned to the function gophy
which will simply return a function call. We are not calling the function simply returning the call to the function gophy
that has the return value as the anonymous function. We will simply have the function in the variable g
rather than the simple value string. So we will have to call the g
variable for actually returning the string.
package main import "fmt" func gophy() func() string{ return func() string{ return "Hello, Gophers!" } } func main() { // using clousure/anonymous function to return a value // that value can be assigned to the variable g := gophy() fmt.Println(g()) }
$ go run simple.go Hello, Gophers!
So, that is how we can call the function g
that will return a string, so we have the function body stored in the variable g
. We can call it as many times as we want.
Variable Scope in Closures
We can even use variables that will remain in the function scope once it is initialized. So, let’s say we have a function that will increment the counter, but if we want to keep the counter the same throughout the program, we might have to use a global variable so as to maintain the context, but with closures, we will retain the value once we have initialized the function call.
In the below example, we are creating the function incrementer
that is a closure with int as the return type. We are initializing the variable counter
that will be acting as the counter in the program, the function returns an anonymous function that will increment the counter and return it.
Here, when we create an instance of the increment
function it basically initializes the counter
to 0
and returns the anonymous function as a call. Now, c
will act as a function that has the counter variable bound to it and we can call c
that will, in turn, call the anonymous function keeping the scope of the counter
variable. So, each time we call the function c
it will increment the counter and thus we keep the counter inside the scope of the function incrementer
in the c
variable.
package main import "fmt" func inrementer() func() int{ counter := 0 return func() int{ counter += 1 return counter } } func main() { c := inrementer() fmt.Println(c()) fmt.Println(c()) fmt.Println(c()) fmt.Println(c()) fmt.Println(c()) }
$go run simple.go 1 2 3 4 5
If we want to extend the functionality, we can even assign the function call c()
to a variable and access the returned value which will be the current state of the counter.
We can even use different scope or closures tied to a particular function, that is we can bind data to a different instances of a closure.
package main import "fmt" func inrementer() func() int{ counter := 0 return func() int{ counter += 1 return counter } } func main() { c1 := inrementer() fmt.Println(c1()) fmt.Println(c1()) fmt.Println(c1()) c2 := inrementer() fmt.Println(c2()) fmt.Println(c2()) fmt.Println(c2()) fmt.Println(c2()) }
$go run simple.go 1 2 3 1 2 3 4
Here we have c1
and c2
forming different closures and thereby we can have different scopes of the variables associated with it. The variable is bound to the instance it which was initialized, so we can see the different closure instances having different values.
Factorial of a function with Closures
We can create some interesting programs with closures, we will implement the calculation of factorial with closures in golang.
This will be a factorial
function that returns an anonymous function with the return type as int
. The function will initialize the variable fact
which will store the actual factorial value and n
as the initial number for calculating the factorial of it.
Inside the anonymous function, we will calculate the factorial and increment the number and simply return the factorial value from the function. The fact
variable will contain the factorial of the number n, so here we can leverage the use of closures as we will maintain the state of the variable fact
and n
from the previous calls or the initialization of the function.
Inside the main
function, we have created the f
variable and called the factorial
function, so that will initialize the fact
and n
of the variable and thereby returning the anonymous function call. Now we can call the variable f
as many times as we want that will simply return the factorial of the number incremented each time we call the function.
package main import "fmt" func factorial() func() int{ fact, n := 1, 1 return func() int{ fact = fact * n n += 1 return fact } } func main() { f := factorial() fmt.Println(f()) fmt.Println(f()) fmt.Println(f()) fmt.Println(f()) fmt.Println(f()) }
$ go run simple.go 1 2 6 24 120
So, we can see that the factorial is getting printed for each call and the number is being incremented at each call.
So that's the basics of closures in golang, we can use closures to keep the content secured and encapsulated from different function calls. We can bind data with closures, with the help of anonymous functions a closure can be constructed and data can be bound to a particular function called scope.
That's it from this part. Reference for all the code examples and commands can be found in the 100 days of Golang GitHub repository.
Conclusion
From this post, we could understand the fundamentals of closures in golang. The basic concept of closures in golang was understood with a few examples. Thank you for reading, if you have any queries or feedback please leave them in the comments or on my social handles. Happy Coding :)