Lesson 7 · 7 of 20
Closures and function values · Go
Use functions as values in Go: function types, functions that return closures, and closures with their own state. Price rules and invoices.

In Go, functions are values: you can store them in variables, pass them as arguments and return them. PriceRule is a function type: any func(float64) float64 fits, which lets you keep a list of rules, rules, and apply them one after another.
percentOff(10) returns an anonymous function that uses pct, a parameter of percentOff. That function is a closure: it remembers the variables from where it was created, even after percentOff has returned. percentOff(10) and percentOff(5) are two different rules, each with its own pct.
addVAT is a function literal stored in a variable. It does not need to be declared as a PriceRule: its type is func(float64) float64, so it goes straight into the list. The inner loop applies the three rules in order: 100 minus 10%, minus 5% and plus 21% VAT gives 103.45.
invoiceNumbers returns a closure that changes n. The variable is not lost when the function returns: it lives as long as the closure does, and every call to next() increments it (INV-001, INV-002…). It is a simple way to keep state without a struct.
Each call to invoiceNumbers creates a fresh n: other starts at REF-001 while next carries on with INV-004. The arguments to fmt.Println are evaluated left to right, which is why REF-001 and REF-002 come out in that order.
Code · main.go
package mainimport "fmt"// PriceRule is a function type: any func(float64) float64 fits.type PriceRule func(float64) float64func percentOff(pct float64) PriceRule { return func(price float64) float64 { return price * (1 - pct/100) } // remembers pct}// invoiceNumbers returns a closure with its own counter, kept between calls.func invoiceNumbers(prefix string) func() string { n := 0 return func() string { n++ return fmt.Sprintf("%s-%03d", prefix, n) }}func main() { addVAT := func(price float64) float64 { return price * 1.21 } // a function value rules := []PriceRule{percentOff(10), percentOff(5), addVAT} next := invoiceNumbers("INV") for _, price := range []float64{100, 40, 25} { final := price for _, apply := range rules { final = apply(final) } fmt.Printf("%s %6.2f -> %6.2f\n", next(), price, final) } other := invoiceNumbers("REF") // a new closure starts its own count fmt.Println(other(), other(), "| main series goes on:", next())}Output · go run main.go
INV-001 100.00 -> 103.45 INV-002 40.00 -> 41.38 INV-003 25.00 -> 25.86 REF-001 REF-002 | main series goes on: INV-004
Key points
- Functions are values: store them, pass them, return them.
- A function type, such as
PriceRule, names a signature. - A closure remembers the variables from where it was created.
- Captured variables survive as long as the closure does.
- Each call to the outer function creates new state.
Exercise
Write minus(amount float64) PriceRule, which subtracts a fixed amount without going below zero, and add minus(5) to rules before the VAT. Check how each invoice total changes.