Why Your Go Closure Doesn't Behave Like JavaScript's (and How to Fix It)

Table of Contents

Writing a Go Closure, Head first.

  • Let’s create a factory function called createAdder that returns the currentValue and add function to add integers to the variable.
  • This demonstrates a classic usecase of a closure.
  • We start by writing a closure in go just as we do in any other scripting language. Below would be the code if we did a head first dive and wrote a closure.
  • We create createAdder function that supposedly returns a lexicographically scoped variable currentValue and a add function that adds a number provided as a parameter to that variable.
main.go
package main
import "fmt"
func createAdder() (int, func(int)) {
currentValue := 0
add := func(val int) {
currentValue += val
}
return currentValue, add
}
func main() {
currentValue, add := createAdder()
fmt.Println("Before:", currentValue)
add(1)
add(2)
fmt.Println("After:", currentValue)
}
  • Lets run it with go run main.go and see the output.
Before: 0
After: 0

Why does it not work?

  • The reason it doesn’t work is unlike languages like javascript or python, go being a compiled language actually supports primitives. In javascript or python everything is as object, even seemingly primitive types.
  • The currentValue which was unpacked at the line no. 16 is a primitive and got updated (when a primitive is reassigned, any previous copies become stale) after we made an add call. But we kept the stale reference and it tried to access that.

Writing a Go closure the right way

  • There are actually two ways we can write a closure.
    1. Write a closure and return a value. The function caller has to always reference the returned value.
    2. Use a pointer reference.

Writing a closure with return value

  • We update our code to always return the updated outer scoped variable.
  • The function caller makes sure post each operation, it captures the newly updated value.
package main
import "fmt"
func createAdder() (int, func(int)) {
func createAdder() (int, func(int) int) {
currentValue := 0
add := func(val int) {
add := func(val int) int {
currentValue += val
return currentValue
}
return currentValue, add
}
func main() {
currentValue, add := createAdder()
fmt.Println("Before:", currentValue)
add(1)
add(2)
currentValue = add(1)
currentValue = add(2)
fmt.Println("After:", currentValue)
}
  • Lets run it again now.
Before: 0
After: 3

Writing a closure with pointer reference

  • We now return a reference of a variable instead and the function caller will treat it as a pointer and deference it to access the actual value
  • This is inline with classic use-case of a closure.
package main
import "fmt"
func createAdder() (int, func(int)) {
func createAdder() (*int, func(int)) {
currentValue := 0
add := func(val int) {
currentValue += val
}
return currentValue, add
return &currentValue, add
}
func main() {
currentValue, add := createAdder()
fmt.Println("Before:", currentValue)
fmt.Println("Before:", *currentValue)
add(1)
add(2)
fmt.Println("After:", currentValue)
fmt.Println("After:", *currentValue)
}

This is true for any compiled language that defines primitives

My avatar

Thanks for reading my blog post! Feel free to check out my other posts or contact me via the social links in the footer.


More Posts