Closures in GoΒΆ

package main

import (
    "fmt"
)

////////////////////////////////////////////////////////////

// Go uses escape analysis to determine that the local variable result should
// not be declared on the stack.
func twice(s string) *string {
    result := s + s // result is a local variable
    return &result  // return a pointer to the result
}

func test1() {
    fmt.Println(*twice("apple"))
    fmt.Println(*twice("down"))
}

////////////////////////////////////////////////////////////

// In Go, functions can returned functions. You can also pass functions as
// parameters to functions.
func makeGreeter1() func() {
    return func() {
        fmt.Println("Hello!")
    }
}

func test2() {
    greet := makeGreeter1()
    greet()
    greet()
}

////////////////////////////////////////////////////////////

func makeGreeter2(s string) func() {
    return func() {
        fmt.Println(s)
    }
}

func test3() {
    greet := makeGreeter2("Hi!")
    greet()
    greet()
}

////////////////////////////////////////////////////////////

func makeGreeter3() func(string) {
    return func(s string) {
        fmt.Println(s + "!")
    }
}

func test4() {
    greet := makeGreeter3()
    greet("Hey")
    greet("Hi")
}

////////////////////////////////////////////////////////////

// makeAdder1 returns a closure, i.e. a special value that contains a function
// *and* references to the free variables that are in the function. A closure
// can be called just like a function.
func makeAdder1() func() int {
    total := 0 // local variable
    inc := func() int {
        total++
        return total
    }

    return inc
}

func test5() {
    next := makeAdder1()
    fmt.Println(next())
    fmt.Println(next())
    fmt.Println(next())
}

////////////////////////////////////////////////////////////

// This example shows that two closures can share the same variable.
func makeAdder2() (func() int, func() int) {
    total := 0 // local variable

    inc := func() int {
        total++
        return total
    }

    dec := func() int {
        total--
        return total
    }

    return inc, dec
}

func test6() {
    next, prev := makeAdder2()
    fmt.Println(next())
    fmt.Println(prev())
    fmt.Println(next())
}

////////////////////////////////////////////////////////////

func makeAdder3() (func() int, func() int, func()) {
    total := 0 // local variable

    inc := func() int {
        total++
        return total
    }

    dec := func() int {
        total--
        return total
    }

    reset := func() {
        total = 0
    }

    return inc, dec, reset
}

func test7() {
    next, prev, reset := makeAdder3()
    fmt.Println(next())
    fmt.Println(prev())
    fmt.Println(next())
    reset()
    fmt.Println(next())
    fmt.Println(prev())
    fmt.Println(next())
}

////////////////////////////////////////////////////////////

// Closures are quite powerful, and could, for instance, be used to implemente
// a kind of object-oriented programming.
func makePerson(initName string, initAge int) (func(string), func() string, func(int), func() int) {
    name := initName
    age := initAge

    setName := func(s string) {
        if len(s) > 0 {
            name = s
        }
    }
    getName := func() string {
        return name
    }

    setAge := func(n int) {
        if n > 0 {
            age = n
        }
    }
    getAge := func() int {
        return age
    }

    return setName, getName, setAge, getAge
}

func test8() {
    setName, getName, setAge, getAge := makePerson("May", 11)
    fmt.Printf("%v, %v\n", getName(), getAge())
    setName("Irene")
    setAge(getAge() + 1)
    fmt.Printf("%v, %v\n", getName(), getAge())
}

////////////////////////////////////////////////////////////

func main() {
    // test1()
    // test2()
    // test3()
    // test4()
    // test5()
    // test6()
    // test7()
    test8()
}