Anonymous Functions & Closures - Practical Go Lessons-24
Anonymous Functions & Closures - Practical Go Lessons-24
com/chap-24-anonymous-functions-and-closures
• What is a closure?
• Function literal
• Lexical context
• Scope
3 Anonymous functions
An anonymous function is similar to a traditional function except that the programmers did not give that function a name.
// anonymous/anon/main.go
package main
import "fmt"
func main() {
// an anonymous function
func() {
// the instructions of the anonymous function
fmt.Println("I am anonymous function")
}()
}
Here we define a function with no name by using the syntax func(){..} and we execute it by adding the two parenthesis () .
So to define an anonymous function and execute it just after, you can use the following syntax :
func(){...}()
1 of 12 02/01/2023, 02:14
Anonymous functions & closures - Practical Go Lessons https://www.practical-go-lessons.com/chap-24-anonymous-functions-and-closures
// anonymous/example-2/main.go
package main
import (
"fmt"
"reflect"
)
func main() {
/**
* func literal not executed
*/
myFunc := func() int {
fmt.Println("I am a func litteral")
return 42
}
// Output : nothing
// the function is not executed
fmt.Println(reflect.TypeOf(myFunc))
// Output : func() int
/**
* func literal invoked
*/
funcValue := func() int {
fmt.Println("I am a func literal invoked")
return 42
}()
// Output: I am a func literal invoked
// the func is executed!
fmt.Println(reflect.TypeOf(funcValue))
// Output : int
What is the most important thing here? Just remember that a function can have no name; you can store a function (not its result) but the
function itself into a variable. This last thing causes a lot of trouble to many developers. We are confused because we are used to storing
values and not functions inside variables!
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
4 Function types
You can define a type based on a function signature. Here is an example taken from the gin module (https://github.com/gin-gonic/gin)
package gin
//...
//...
2 of 12 02/01/2023, 02:14
Anonymous functions & closures - Practical Go Lessons https://www.practical-go-lessons.com/chap-24-anonymous-functions-and-closures
package http
// ...
type HandlerFunc func(ResponseWriter, *Request)
//...
A function type designates all functions with the same parameters and results.1. Once you have defined your type, you can create a variable
that has a function type. Let’s take an example :
var f Funky
f = func(s string){
// my function defined
log.Printf("Funky %s",s)
}
f("Groovy")
Funky designate all functions with a single string parameter and no results. ( func(string) )
• Used a parameters
• Used as results
• Assigned to a variable
6 Closures
An anonymous function defined into another function F can use elements that are not defined in its scope but in the scope of F .
Anonymous functions can keep references to its surrounding state. A closure is formed when a function references variables not defined in its
own scope.4
6.1 Example
Here is an example of a closure :
3 of 12 02/01/2023, 02:14
Anonymous functions & closures - Practical Go Lessons https://www.practical-go-lessons.com/chap-24-anonymous-functions-and-closures
// anonymous/first-closure/main.go
package main
import "fmt"
func main() {
p := printer()
p()
// Print n. 1
p()
// Print n. 2
p()
// Print n. 3
}
We have a function that is called printer . The signature of this function is printer() func() . This means that this function will return itself
a function. func() means that we return a function that returns nothing. We could have the following syntax as return type : func() int
this would mean that our returned function will return itself an integer. Let’s jump into the declaration of the function printer . First, we
define the variable k which is initialized with the value 1. And then we return an anonymous function. Into this function, we use
fmt.Printf . to display into the standard output "Print n. %d\n" where %d is replaced by the value of k .
Here we :
• Then in the main function, we will assign to p the return value of printer (which is a function). After that, we will use p .
• Remember that p is a function, so to execute it, we have just to add parenthesis at the end :
6.2 Scopes
• We say that this anonymous function captures it’s lexical context.
• The function keeps a memory of the variables that are used in its environment.
4 of 12 02/01/2023, 02:14
Anonymous functions & closures - Practical Go Lessons https://www.practical-go-lessons.com/chap-24-anonymous-functions-and-closures
• The scope of a function (or lexical environment) is a region where you can use all the variables (types, constants) that you have
declared in it.
• The enclosing function defines an outer scope. In our case, we define the variable k in the outer scope of the enclosing function. k
is then used in the returned function scope (the inner scope).
• We use a variable in the inner scope that we declared in the outer scope.
• The closures stores a reference to the variable that we defined in the outer scope.
• That’s why when we execute our closure several time the value k is incremented. The closure holds the reference, and each time we
execute it, it will modify the underlying value.
// sort package
In the two previous signature, we see that sort.Search and sort.Slice functions are requiring both a function to work. The function is
used internally to do the sorting job.
5 of 12 02/01/2023, 02:14
Anonymous functions & closures - Practical Go Lessons https://www.practical-go-lessons.com/chap-24-anonymous-functions-and-closures
// anonymous/input-parameters/main.go
package main
import (
"fmt"
"sort"
)
func main() {
scores := []int{10, 89, 76, 3, 20, 12}
// ascending
sort.Slice(scores, func(i, j int) bool { return scores[i] < scores[j] })
fmt.Println(scores)
// Output : [3 10 12 20 76 89]
// descending
sort.Slice(scores, func(i, j int) bool { return scores[i] > scores[j] })
fmt.Println(scores)
// Output : [89 76 20 12 10 3]
}
• We define a variable scores , which is a slice of integers. They represent the winning score for 6 “gophing” games (a new sport
practiced only by gophers).
• The scores are not sorted at all. The objective is to sort them following an order.
• Its signature requires a slice and a function. The function will define how the runtime will execute the sort. The functions take two
parameters (integers); they represent two indexes. It returns a boolean.
• We have here a closure because the function uses the variable scores , even if it is not defined in its inner scope.
The element at index i will be placed before the element at index j if the element at index i is less than the element at index j .
[3 10 12 20 76 89]
In the second call to sort.Slice we are giving as input the following function :
The element at index i will be placed before the element at index j if the element at index i is greater than the element at index j .
The result is a descending ordered slice :
[89 76 20 12 10 3]
Before explaining the technique, we will build a simple web server (you can skip the paragraph if you already know how to do it)
// anonymous/wrappers/main.go
6 of 12 02/01/2023, 02:14
Anonymous functions & closures - Practical Go Lessons https://www.practical-go-lessons.com/chap-24-anonymous-functions-and-closures
The homepageHandler function takes a response writer ( http.ResponseWriter ) (to prepare the response) and the request ( *http.Request ).
// anonymous/wrappers/main.go
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/homepage", homepageHandler)
err := http.ListenAndServe(":3000", nil)
if err != nil {
log.Fatal(err)
}
}
func homepageHandler(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintln(writer, "Welcome to my homepage")
fmt.Fprintln(writer, "I am max")
The brutal way to do this would be to add a counter in each of your handlers.
But there is a clever way: you can create a generic wrapper that all your handlers can use. Your wrapper should take as input a handler and
return a handler. That way, you can have the following syntax :
http.HandleFunc("/homepage", trackVisits(homepageHandler))
This is elegant! We understand that here we track the visits and then we execute the handler. Let’s implement the trackVisits function :
• The return type of trackVisits must have the same signature as the one expected by the second input parameter of
http.HandleFunc .
• In the function body, we are returning an anonymous function. This anonymous function will first print that we have one visit and then
launch the original handler’s execution.
This technique is very powerful. It gives us the power to create generic wrappers that we can use on several endpoints: it avoids code
duplication.
http.HandleFunc("/homepage", trackVisits(homepageHandler))
http.HandleFunc("/cv", trackVisits(cvHandler))
http.HandleFunc("/projects", trackVisits(projectsHandler))
http.HandleFunc("/projects", authenticate(trackVisits(projectsHandler)))
7 of 12 02/01/2023, 02:14
Anonymous functions & closures - Practical Go Lessons https://www.practical-go-lessons.com/chap-24-anonymous-functions-and-closures
Usually, a library offers several “options”. With this pattern, options are provided with functions.
7.3.1 Example
Let’s take an example of a fake authentication library :
if auth.IsValidJWT("invalid") {
log.Println("mmmm... maybe we should use another lib.")
}
• We pass to the function a string "test" and two other parameters, which are
authenticator.UnverifiedEmailAllowed()
authenticator.WithCustomJwtDuration(time.Second)
• Those two additional parameters are function calls that will return a value.
8 of 12 02/01/2023, 02:14
Anonymous functions & closures - Practical Go Lessons https://www.practical-go-lessons.com/chap-24-anonymous-functions-and-closures
// anonymous/functionalOptions/authenticator/authenticator.go
package authenticator
import (
"time"
)
• For each option, we create an exported function that will return an Option
◦ For the option “allow unverified email” we have the following function :
func UnverifiedEmailAllowed() Option {
return func(options *options) {
options.allowUnverifiedEmail = true
}
}
9 of 12 02/01/2023, 02:14
Anonymous functions & closures - Practical Go Lessons https://www.practical-go-lessons.com/chap-24-anonymous-functions-and-closures
• This function is variadic, because it can be invoked with zero or more arguments of type Option .
• Inside New :
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
8 Test yourself
Questions
1. True or False? You cannot assign an anonymous function to a variable.
4. Fill in the blank : “An ______ can be declared with a type ____.”
5. Can an anonymous function use a variable not defined in its body or parameter list?
Answers
1. True or False? You cannot assign an anonymous function to a variable.
1. False
1. This is true
func main(){
func insideFunction(){
}
}
func main(){
f := func(s string){
func insideFunction(){
}
test()
}
}
1. False
4. Fill in the blank : “An ______ can be declared with a type ____.”
1. Wrapping handlers
6. Can an anonymous function use a variable not defined in it’s body or parameter list?
10 of 12 02/01/2023, 02:14
Anonymous functions & closures - Practical Go Lessons https://www.practical-go-lessons.com/chap-24-anonymous-functions-and-closures
1. Yes
9 Key takeaways
• To define an anonymous function, use a function literal
◦ Ex :
• An anonymous function can use elements (variables, types, constants) that are defined in its surrounding scope
◦ We say that the anonymous function captures the lexical context of the surrounding function.
1. https://golang.org/ref/spec#Function_types↩
3. This term was introduced indirectly in 1967 by CHRISTOPHER STRACHEY, see[@strachey2000fundamental], section 3.5.1. First
and second class objects↩
Bibliography
• [strachey2000fundamental] Strachey, Christopher. 2000. “Fundamental Concepts in Programming Languages.” Higher-Order and
Symbolic Computation 13 (1): 11–49.
Previous Next
Table of contents
Did you spot an error ? Want to give me feedback ? Here is the feedback page! ×
Newsletter:
Like what you read ? Subscribe to the newsletter.
11 of 12 02/01/2023, 02:14
Anonymous functions & closures - Practical Go Lessons https://www.practical-go-lessons.com/chap-24-anonymous-functions-and-closures
@ my@email.com
Practical Go Lessons
By Maximilien Andile
Copyright (c) 2023
Follow me Contents
Posts
Book
Support the author Video Tutorial
About
The author
Legal Notice
Feedback
Buy paper or digital copy
Terms and Conditions
12 of 12 02/01/2023, 02:14