First class functions in Go

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?


Solution 1:

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:

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:

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:

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