First class functions in Go

GoFunctional Programming

Go Problem Overview


I come from JavaScript which has first class function support. For example you can:

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

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

Go Solutions


Solution 1 - Go

Go Language and Functional Programming might 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 is the blog owner: Dethe Elza (not me)

Solution 2 - Go

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

output:

222111
222111

Solution 3 - Go

The related section from the specification: Function types.

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

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

Declaring a variable of function type which 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's write a function which returns an adder function. This adder function should take 2 parameters of type float64 and should returns the sum of those 2 numbers when called:

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

Let's write a function which has 3 parameters, first 2 being of type float64, and the 3rd being a function value, a function that takes 2 input parameters of type float64 and produces a value of float64 type. And the function we're writing will call the function value that is passed to it as parameter, and using the first 2 float64 values as arguments for the function value, and returns the result that the passed function value returns:

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

Let's 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 of course you can use the Short variable declaration when creating adder:

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 function type, you can use that too.

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

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

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

Try it on the Go Playground.

Note:

Note that the parameter names are not part of the type, the type of 2 functions having the same parameter and result types is identical regardless of the names of the parameters. But know that within a list of parameters or results, the names must either all be present or all be absent.

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

Solution 4 - Go

While you can use a 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

Solution 5 - Go

Just a brainteaser with recursive function definition for chaining middlewares in a web app.

First, the toolbox:

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 see type Chain is a function that returns another function of the same type Chain (How first class is that!).

Now some tests to see it 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, this was just a brainteaser to show we can use first class functions in Go in different ways. Personally I use chi nowadays as router and for handling middlewares.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDrew LeSueurView Question on Stackoverflow
Solution 1 - Gouser180100View Answer on Stackoverflow
Solution 2 - Gouser2532519View Answer on Stackoverflow
Solution 3 - GoiczaView Answer on Stackoverflow
Solution 4 - GoCarlView Answer on Stackoverflow
Solution 5 - GoKaveh ShahbazianView Answer on Stackoverflow