0% found this document useful (0 votes)
52 views

Anonymous Functions & Closures - Practical Go Lessons-24

- Anonymous functions allow defining functions without naming them. They can be defined using a function literal like func(){} and executed immediately by adding parentheses. - Closures are formed when an anonymous function references variables from the enclosing scope. This allows the function to access those variables even when the enclosing function has returned. - Functions in Go are first-class citizens, meaning they can be used as return values and passed as arguments like any other variable. This enables patterns like returning functions from other functions to create closures.

Uploaded by

looklo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
52 views

Anonymous Functions & Closures - Practical Go Lessons-24

- Anonymous functions allow defining functions without naming them. They can be defined using a function literal like func(){} and executed immediately by adding parentheses. - Closures are formed when an anonymous function references variables from the enclosing scope. This allows the function to access those variables even when the enclosing function has returned. - Functions in Go are first-class citizens, meaning they can be used as return values and passed as arguments like any other variable. This enables patterns like returning functions from other functions to create closures.

Uploaded by

looklo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 12

Anonymous functions & closures - Practical Go Lessons https://www.practical-go-lessons.

com/chap-24-anonymous-functions-and-closures

Chapter 24: Anonymous functions & closures

1 What will you learn in this chapter?


• What is an anonymous function?

• How to create an anonymous function.

• How to call an anonymous function.

• What is a closure?

• What is a function type?

2 Technical concepts covered


• Anonymous function

• 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.

To create an anonymous function, we use a function literal :

// 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

• To define the function, use the syntax : func(){..}

• To define and run it : func(){..}()

Let’s take another example

// 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

//...

type HandlerFunc func(*Context)

//...

And here is an example taken from the standard http package :

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 :

type Funky func(string)

var f Funky
f = func(s string){
// my function defined
log.Printf("Funky %s",s)
}
f("Groovy")

• We define a new exported type Funky .

Funky designate all functions with a single string parameter and no results. ( func(string) )

• We create a variable f of type Funky .

• Then we assign to the variable f an anonymous function

• And then, we execute f with f("Groovy")

5 Functions are “first-class citizens”


In some programming languages2 functions are “first-class citizens”3 allowing them to be :

• Used a parameters

• Used as results

• Assigned to a variable

Go functions are considered as first-class objects. It means that :

• Functions can be passed as arguments to other functions

• You can return functions from other functions

• You can bind functions to variables

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 printer() func() {


k := 1
return func() {
fmt.Printf("Print n. %d\n", k)
k++
}
}

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 :

• define the variable k in the outer function

• we return a new anonymous function that makes use of k .

Hence we have created a closure.

• 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 :

p() // we execute p, the closure

The type of p is func() .

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

Inner and outer scope[fig:Inner-and-outer]

• 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.

7 Some common use cases of closures


7.1 Function parameters
You can pass a function as a parameter of another function. In the sort package (part of the standard library), several functions accept
functions as parameters :

// sort package

// Search for an index of a specific value using binary search


func Search(n int, f func(int) bool) int

// sort a slice by using the provided function (less)


func Slice(slice interface{}, less func(i, j int) bool)

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.

Let’s take an example :

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.

• We will use the function sort.Slice .

• 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.

In our first example, the less function is :

func(i, j int) bool { return scores[i] < scores[j] }

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 .

As a result, our slice will be sorted in ascending order :

[3 10 12 20 76 89]

In the second call to sort.Slice we are giving as input the following function :

func(i, j int) bool { return scores[i] > scores[j] }

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]

7.2 Wrappers : http.HandleFunc


We can use closures to wrap other functions.

Before explaining the technique, we will build a simple web server (you can skip the paragraph if you already know how to do it)

7.2.1 Setting of a basic web server


In the following code block, we are specifying that when a request is sent to the endpoint "/homepage" the function homepageHandler will
be executed.

// anonymous/wrappers/main.go

func homepageHandler(writer http.ResponseWriter, request *http.Request) {


fmt.Fprintln(writer, "Welcome to my homepage")
fmt.Fprintln(writer, "I am max")
}

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 ).

Here is a minimal web server example :

// 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")

7.2.2 The wrapper technique


Imagine that your web server has several endpoints. and you want to keep track of visits on each endpoint.

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 :

func trackVisits(handler func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {


return func(writer http.ResponseWriter, request *http.Request) {
// track the visit
fmt.Println("one visit !")
// call the original handler
handler(writer, request)
}
}

• The trackVisits function takes a handler as parameter and returns a handler .

• 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))

Is it also possible to chain the wrappers :

http.HandleFunc("/projects", authenticate(trackVisits(projectsHandler)))

by defining other wrappers :

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

func authenticate(handler func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {


return func(writer http.ResponseWriter, request *http.Request) {
// check that request is authenticated
fmt.Println("authenticated !")
// call the original handler
handler(writer, request)
}
}

7.3 Functional options pattern


Dave Cheney has described this pattern5. It is a pattern that allows you to improve the API of your libraries.

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 :

auth := authenticator.New("test", authenticator.UnverifiedEmailAllowed(), authenticator.WithCustomJwtDuration(time.Second))

if auth.IsValidJWT("invalid") {
log.Println("mmmm... maybe we should use another lib.")
}

• We call authenticator.New to initialize a new authenticator

• 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.

You see here that options are set with functions.

7.3.2 How is it made inside?

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"
)

type options struct {


jwtDuration time.Duration
allowUnverifiedEmail bool
allowUnverifiedPhone bool
}

type Option func(*options)

func WithCustomJwtDuration(duration time.Duration) Option {


return func(options *options) {
options.jwtDuration = duration
}
}

func UnverifiedEmailAllowed() Option {


return func(options *options) {
options.allowUnverifiedEmail = true
}
}

func UnverifiedPhoneAllowed() Option {


return func(options *options) {
options.allowUnverifiedEmail = true
}
}

type Authenticator struct {


name string
options options
}

func New(name string, opts ...Option) *Authenticator {


options := options{
jwtDuration: time.Hour,
}
for _, opt := range opts {
opt(&options)
}
return &Authenticator{name: name, options: options}
}

func (a Authenticator) IsValidJWT(jwt string) bool {


return false
}

• First an options struct is created

◦ This struct will keep track of the options set.

◦ Each option is a field (unexported).

• A function type is created type Option func(*options)

• 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
}
}

New will take as second parameter Option s

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 :

◦ First, the default values are set.

◦ Then we iterate over opts (which is a slice of Option )

◦ At each iteration, we get a function that set an option

◦ We simply execute the function with opt(&options)

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.

2. True or False? You cannot define a named function inside a function

3. True or False? You cannot define an anonymous function inside a function

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

2. You can assign an anonymous function to a variable

f35 := func()uint{ return 35}

2. True or False? You cannot define a named function inside a function

1. This is true

2. The following snippets won’t compile :

func main(){
func insideFunction(){

}
}

func main(){
f := func(s string){
func insideFunction(){

}
test()
}
}

3. True or False ? You cannot define an anonymous function inside a function

1. False
4. Fill in the blank : “An ______ can be declared with a type ____.”

1. An anonymous function can be declared with a type literal.


5. Name one common use case of closures.

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

2. An anonymous function can use elements defined in its enclosing function.

9 Key takeaways
• To define an anonymous function, use a function literal

func(){ fmt.Println("my anonymous function")}

• A function literal can be assigned to a variable

myFunc := func(){ fmt.Println("my anonymous function")}

• To define & immediately call an anonymous function use parenthesis :

func(){ fmt.Println("my anonymous function")}()

◦ Here we define and execute this anonymous function


• A function type designate all functions with the same parameters and results

◦ The zero value of the function type is nil

◦ Ex :

type Option func(*options)

• Functions are “first-class citizen”.

◦ They can be passed as parameter

◦ They can be returned as result

◦ They can be assigned to a variable

• 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↩

2. Especially functional languages↩

3. This term was introduced indirectly in 1967 by CHRISTOPHER STRACHEY, see[@strachey2000fundamental], section 3.5.1. First
and second class objects↩

4. See : https://en.wikipedia.org/wiki/Closure_(computer_programming)#Lexical_environment, https://tour.golang.org/moretypes/25↩

5. See: https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis and https://dave.cheney.net/2016/11/13/do-not-fear-


first-class-functions↩

Bibliography
• [strachey2000fundamental] Strachey, Christopher. 2000. “Fundamental Concepts in Programming Languages.” Higher-Order and
Symbolic Computation 13 (1): 11–49.

Previous Next

Errors JSON and XML

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

I will keep you informed about the book updates.

@ 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

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy