First Class Functions in Go

I come from JavaScript, which supports a first class function. For example, you can:

  • pass a function as a parameter to another function
  • returns a function from a function.

Can someone give me an example of how I will do this in Go?

+47
functional-programming go
Dec 05 '10 at 8:45
source share
5 answers

Go Language and functional programming can help. From this blog post:

package main import fmt "fmt" type Stringy func() string func foo() string{ return "Stringy function" } func takesAFunction(foo Stringy){ fmt.Printf("takesAFunction: %v\n", foo()) } func returnsAFunction()Stringy{ return func()string{ fmt.Printf("Inner stringy function\n"); return "bar" // have to return a string to be stringy } } func main(){ takesAFunction(foo); var f Stringy = returnsAFunction(); f(); var baz Stringy = func()string{ return "anonymous stringy\n" }; fmt.Printf(baz()); } 

Author - blog owner: Dethe Elza (not me)

+38
Dec 05 2018-10-12T00:
source share
 package main import ( "fmt" ) type Lx func(int) int func cmb(f, g Lx) Lx { return func(x int) int { return g(f(x)) } } func inc(x int) int { return x + 1 } func sum(x int) int { result := 0 for i := 0; i < x; i++ { result += i } return result } func main() { n := 666 fmt.Println(cmb(inc, sum)(n)) fmt.Println(n * (n + 1) / 2) } 

exit:

 222111 222111 
+27
Jun 28 '13 at 15:52
source share

Related section from the specification: Function Types .

All the other answers here first declare a new type that is good (practice) and makes your code easier to read, but be aware that this is not a requirement.

You can work with function values ​​without declaring a new type for them, as shown in the example below.

The declaration of a function type variable that has 2 parameters of type float64 and has one return value of type float64 looks like this:

 // Create a var of the mentioned function type: var f func(float64, float64) float64 

Let him write a function that returns the function of the adder. This adder function should take 2 parameters of type float64 and should return the sum of these two numbers when called:

 func CreateAdder() func(float64, float64) float64 { return func(x, y float64) float64 { return x + y } } 

Let a function be written that has 3 parameters, the first 2 are of type float64 , and 3 is the value of the function, a function that takes 2 input parameters of type float64 and produces a value of type float64 , And the function we write will call the value of the function that is passed to it as a parameter, and using the first 2 float64 values ​​as arguments for the function value and returns the result returned by the passed function value:

 func Execute(a, b float64, op func(float64, float64) float64) float64 { return op(a, b) } 

See our previous examples in action:

 var adder func(float64, float64) float64 = CreateAdder() result := Execute(1.5, 2.5, adder) fmt.Println(result) // Prints 4 

Note that when creating adder you can use short variable declarations :

 adder := CreateAdder() // adder is of type: func(float64, float64) float64 

Try these examples on the Go Playground .

Using an Existing Function

Of course, if you already have a function declared in a package with the same type of function, you can also use this.

For example, math.Mod() has the same type of function:

 func Mod(x, y float64) float64 

So, you can pass this value to our Execute() function:

 fmt.Println(Execute(12, 10, math.Mod)) // Prints 2 

Seal 2 , because 12 mod 10 = 2 . Note that the name of an existing function acts as the value of the function.

Try it on the go playground .

Note:

Note that parameter names are not part of the type, type 2 functions that have the same parameter and result types are identical regardless of parameter names. But be aware that names must either be present in the list of parameters or results, or not at all.

So, for example, you can also write:

 func CreateAdder() func(P float64, Q float64) float64 { return func(x, y float64) float64 { return x + y } } 

Or:

 var adder func(x1, x2 float64) float64 = CreateAdder() 
+5
Jul 31 '15 at 11:28
source share

As long as you can use var or declare a type, you don't need to. You can do this quite simply:

 package main import "fmt" var count int func increment(i int) int { return i + 1 } func decrement(i int) int { return i - 1 } func execute(f func(int) int) int { return f(count) } func main() { count = 2 count = execute(increment) fmt.Println(count) count = execute(decrement) fmt.Println(count) } //The output is: 3 2 
+1
Jun 11 '16 at 8:37
source share

Just an analyst with a recursive function definition to bind middlewares in a web application.

First, the toolbar:

 func MakeChain() (Chain, http.Handler) { nop := http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {}) var list []Middleware var final http.Handler = nop var f Chain f = func(m Middleware) Chain { if m != nil { list = append(list, m) } else { for i := len(list) - 1; i >= 0; i-- { mid := list[i] if mid == nil { continue } if next := mid(final); next != nil { final = next } else { final = nop } } if final == nil { final = nop } return nil } return f } return f, final } type ( Middleware func(http.Handler) http.Handler Chain func(Middleware) Chain ) 

As you can see, the Chain type is a function that returns another function of the same Chain type (Like the first class it is!).

Now a few tests to see this in action:

 func TestDummy(t *testing.T) { c, final := MakeChain() c(mw1(`OK!`))(mw2(t, `OK!`))(nil) log.Println(final) w1 := httptest.NewRecorder() r1, err := http.NewRequest("GET", "/api/v1", nil) if err != nil { t.Fatal(err) } final.ServeHTTP(w1, r1) } func mw2(t *testing.T, expectedState string) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { val := r.Context().Value(contextKey("state")) sval := fmt.Sprintf("%v", val) assert.Equal(t, sval, expectedState) }) } } func mw1(initialState string) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := context.WithValue(r.Context(), contextKey("state"), initialState) next.ServeHTTP(w, r.WithContext(ctx)) }) } } type contextKey string 

Again, it was just a motive to show that we can use the first-class functions in Go in different ways. Personally, I am using chi now as a router and for handling intermediaries.

0
Feb 21 '17 at 11:26
source share



All Articles