Why Your Go Closure Doesn't Behave Like JavaScript's (and How to Fix It)
Lets Golang! 1 / 1
3 min read
Table of Contents
Writing a Go Closure, Head first.
- Let’s create a factory function called
createAdderthat returns thecurrentValueandaddfunction to add integers to the variable. - This demonstrates a classic usecase of a closure.
- We start by writing a closure in
gojust 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
createAdderfunction that supposedly returns a lexicographically scoped variablecurrentValueand aaddfunction that adds a number provided as a parameter to that variable.
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.goand see the output.
Before: 0After: 0Why does it not work?
- The reason it doesn’t work is unlike languages like javascript or python,
gobeing a compiled language actually supports primitives. In javascript or python everything is as object, even seemingly primitive types. - The
currentValuewhich was unpacked at the line no.16is a primitive and got updated (when a primitive is reassigned, any previous copies become stale) after we made anaddcall. 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.
- Write a closure and return a value. The function caller has to always reference the returned value.
- 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: 0After: 3Writing 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 ¤tValue, 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