Go Closure

What are closures?

From Wikipedia

A closure is a data structure storing a function together with an environment, a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or storage location the name was bound to at the time the closure was created.

What does that mean?

In simple words, it means that a function can contain (or return) another function which is called closure. The unique property of this function (closure) is that, it has access to the variables which are accessible in the function that contains (or returned) this closure.

Not very clear?

function A has a local variable “a".
function A contains (or returns) another function, let’s call it B.
function B has access to the variable “a”.

If you have returned the function B, every time you call the function B, even though the function A is done executing, function B still can access and can manipulate the variable “a”.

The function B that we are referring to is a Closure.

Closures in Go

Go supports closures. Go also treats functions as first class citizens. We can assign functions to a variable, return functions from a function, pass functions to a function as parameters.

So, let’s see the famous tour.golang.org example of a closure in Go.

func adder() func(int) int {
    sum := 0
    return func(x int) int {
        sum += x
        return sum
    }
}

func main() {
    pos, neg := adder(), adder()
    for i := 0; i < 10; i++ {
        fmt.Println(
            pos(i),
            neg(-2*i),
        )
    }
}

In the above example you could see that all though the variable “sum" is declared in the function adder, it is still used in the closures held by “pos" and “neg”.

Use case

The above example was one of the use case. Since I am new to closures, I always had a question while coding - “how do I realise if I have to use a closure in this case?”

Let’s consider a scenario where we need a struct that should be initialised with a number and it has a method that returns two values, one incremented and another decremented each time the method is called and that is the only functionality you need from this struct.

How would we do it?

Struct way

type IncDecStruct struct {
    inc         int
    dec         int
}

func getIncDecStruct(begin int) IncDecStruct {
    incDecStruct := IncDecStruct{begin, begin}
    return incDecStruct
}

func (i *IncDecStruct) IncDec() (int, int) {
    i.dec = i.dec - 1
    i.inc = i.inc + 1
    return i.inc, i.dec
}

func main() {
    a := getIncDecStruct(3)
    fmt.Println(a.IncDec())
    fmt.Println(a.IncDec())
    fmt.Println(a.IncDec())

    b := getIncDecStruct(10)
    fmt.Println(b.IncDec())
    fmt.Println(b.IncDec())
    fmt.Println(b.IncDec())
}

You can run this code here to see the output.

Closure way

Let’s check if closures could help us ease this.

func IncDec(begin int) func() (int, int) {
    inc := begin
    dec := begin
    return func() (int, int) {
        inc = inc + 1
        dec = dec - 1
        return inc, dec
    }
}

func main() {
    closure1 := IncDec(3)
    fmt.Println(closure1())
    fmt.Println(closure1())
    fmt.Println(closure1())
     
    closure2 := IncDec(10)
    fmt.Println(closure2())
    fmt.Println(closure2())
    fmt.Println(closure2())
}

The above code can be run here to see the output.

It’s easier this way, isn’t it? So, when you are creating a struct only because of its only method, then think about closures. You can store the contexts in a closure rather than a struct and its method.

comments powered by Disqus
comments powered by Disqus