Let's look at a few simple code examples to understand the difference between an anonymous function and a closure.
Here's a typical named function:
func namedGreeting(name string) {
fmt.Printf("Hey %s!n", name)
}
The following is an example of the anonymous function:
func anonymousGreeting() func(string) {
return func(name string) {
fmt.Printf("Hey %s!n", name)
}
}
Now, let's call them both and call an anonymous inline function to say Hey to Cindy:
func main() {
namedGreeting("Alice")
greet := anonymousGreeting()
greet("Bob")
func(name string) {
fmt.Printf("Hello %s!n", name)
}("Cindy")
}
The output will be as follows:
Hello Alice!
Hello Bob!
Hello Cindy!
Now, let's look at a closure named greeting and see the difference between it and the anonymousGreeting() function.
Since the closure function is declared in the same scope as the msg variable, the closure has access to it. The msg variable is said to be in the same environment as the closure; later, we'll see that a closure's environment variables and data can be passed around and referenced at a later time during a program's execution:
func greeting(name string) {
msg := name + fmt.Sprintf(" (at %v)", time.Now().String())
closure := func() {
fmt.Printf("Hey %s!n", msg)
}
closure()
}
func main() {
greeting("alice")
}
The output will be as follows:
Hey alice (at 2017-01-29 12:29:30.164830641 -0500 EST)!
In the next example, instead of executing the closure in the greeting() function, we will return it and assign its return value to the hey variable in the main function:
func greeting(name string) func() {
msg := name + fmt.Sprintf(" (at %v)", time.Now().String())
closure := func() {
fmt.Printf("Hey %s!n", msg)
}
return closure
}
func main() {
fmt.Println(time.Now())
hey := greeting("bob")
time.Sleep(time.Second * 10)
hey()
}
The output will be as follows:
2017-01-29 12:42:09.767187225 -0500 EST
Hey bob (at 2017-01-29 12:42:09.767323847 -0500 EST)!
Note that the timestamp is calculated when the msg variable is initialized, at the time the greeting("bob") value is assigned to the hey variable.
So, 10 seconds later, when greeting is called and the closure is executed, it will reference the message that was created 10 seconds ago.
This example shows how closures preserve state. Instead of manipulating the state in the outside environment, closures allow states to be created, passed around, and subsequently referenced.
With functional programming, you still have a state, but it's just passed through each function and is accessible even when the outer scopes, from where they originated, have already exited.
Later in this book, we'll see a more realistic example of how closures can be leveraged to maintain a context of application resources required by an API.
Another way to speed up our recursive Fibonacci function is to use Go's concurrency constructs.