Divyansh
Divyansh

Reputation: 166

Does Golang automatically assign variables as arguments when writing function closures?

Here is the code I'm referring to:

package main

import "fmt"

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),
        )
    }
}

Following is the output when it is run:

    0 0
    1 -2
    3 -6
    6 -12
    10 -20
    15 -30
    21 -42
    28 -56
    36 -72
    45 -90

I don't get how x is being assigned in the return statement in the adder function? It does not seem to be passed anywhere in the function. I also don't get how the sum variable works. Shouldn't it get reset everytime the function adder is called and be assigned the value 0?

Upvotes: 0

Views: 922

Answers (2)

torek
torek

Reputation: 488183

Go handles first-class functions and closures in a pretty typical / standard way. For some good background on closures in general, see the Wikipedia article. In this case, calling adder itself:

  1. Creates the int object named sum with value 0.
  2. Returns a closure: a function-like thingy1 that, when called, has access to the variable sum.

The particular function-like thingy that adder returns, which its caller captures in an ordinary variable, is a function that takes one argument. You then call it, passing the one argument. There's nothing special about this argument-passing: it works the same way as it would anywhere else. Inside the function-like thingy, using the variable x gets you the value that the caller passed. Using the name sum gets you the captured int object, whatever its value is. Returning from the function leaves the captured int still captured, so a later call to the same function-like thingy sees the updated int in sum.

By calling adder twice, you get two slightly-different function-like thingies: each one has its own private sum. Both of these private sums are initially zero. Calling the function-like thingy whose value you've saved in pos gets you the function that uses one of them. Calling the slightly-different function-like thingy whose value you've saved in neg gets you the function that uses the other one.


1There's no real difference between this "function-like thingy" and an actual function except that this particular function-like thingy doesn't have a name by which you can invoke it. That's more or less what it means to have first-class functions.


If you're stuck on readability issues...

The original form of this is:

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

Let's rewrite this with a few type names and other syntactic changes that leave the core of the code the same. First, let's make a name that means func(int) int:

type adderClosure func(int) int

Then we can use that to rewrite adders first line:

func adder() adderClosure {
    ...
}

Now let's make a local variable inside adder to hold the function we're going to return. To be explicit and redundant, we can use this type again:

    var ret adderClosure // not good style: just for illustration

Let's now assign that variable to our closure by doing this:

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

and then we can return ret to return the closure. Here's the complete code on the Go Playground.

Upvotes: 2

Kevin Hutchinson
Kevin Hutchinson

Reputation: 2373

The sum variable is inside each of the two closures when you assign pos and neg. The sum in the pos closure is updated by adding 1, 2, 3, 4 (fibonacci style) while the sum in the neg closure is updated by subtracting 2*1, 2*2, 2*3, 2*4 in each of the loop iterations.

Or, in more detail: pos := adder() assigns to pos a function having a closure on sum where sum is 0 to begin. Then whenever you call the function pos, it will updated sum accordingly. The exact same is true with neg, and any other similar assignment.

Here's some similar (simpler) code in JavaScript to run in your browser console:

function adder() {
  var sum = 0;
  return function(i) {
    sum += i;
    return sum;
  }
}

var pos = adder();
console.log( pos(1) ); // add 1 to 0 (1)
console.log( pos(2) ); // add 2 to 1 (3)
console.log( pos(3) ); // add 3 to 3 (6)
console.log( pos(4) ); // add 4 to 6 (10)

Here's some background about Closures in JavaScript: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

Hope this helps.

Upvotes: 0

Related Questions