Higher-order functions are a fundamental concept in functional programming, and Go is no exception. In some time ago we explored how to implement some of the higher order functions in Python,you can check out the video here .
In this tutorial we will learn how to implement them in Go.
In Go, higher-order functions are functions that take other functions as arguments or return functions as output. In this article, we’ll explore how to implement three common higher-order functions in Go: map
, reduce
, and filter
. We’ll also touch on the zip
function, which is not strictly a higher-order function but is often used in conjunction with higher-order functions.
Map Function
The map
function takes a function and a slice of inputs (or sequence), and applies the function to each input in the slice, returning a new slice of outputs. Here’s an example implementation of map
in Go:
First we will implement a map function that accepts only integer and return a slice of integer and then we will make it more robust to support other data types via Go Generics.
func myMap(sequence []int, fn func(int) int) []int { results := []int{} for _, i := range sequence { results = append(results, fn(i)) } return results }
How do we use this our custom map function
package main import "fmt" // our function func multiply6(a float64) float64 { return a * 6 } func main(){ results := myMap([]int{2,4,6,7},multiply6) fmt.Println(results) }
How do we support other data types? To do so we will use Go Generics (T) and create a generic function
func myMap[T, U any](fn func(T) U, inputs []T) []U { outputs := make([]U, len(inputs)) for i, input := range inputs { outputs[i] = fn(input) } return outputs }
This implementation takes a function fn
that takes a single input of type T
and returns a single output of type U
. It also takes a slice of inputs inputs
of type []T
. The function returns a new slice of outputs outputs
of type []U
.
The order of the argument in this function was changed to make it more like how map is used in python.
To use map
, we can define a function that takes a string and returns its uppercase version, and then apply it to a slice of strings:
func toUpper(s string) string { return strings.ToUpper(s) } inputs := []string{"hello", "world", "go"} outputs := myMap(toUpper, inputs) fmt.Println(outputs) // Output: ["HELLO", "WORLD", "GO"] // or with support for int results := myMap(multiply6,[]int{2,4,6,7})
Reduce
The reduce
function takes a function, an initial value, and a slice of inputs, and applies the function to each input in the slice, starting with the initial value. The function returns the final result of the reduction. Here’s an example implementation of reduce
in Go:
func myReduce[T, V any](fn func(V, T) V,sequence []T, initValue V) V { acc := initValue for _, i := range sequence { fmt.Println("Initial", acc) acc = fn(acc, i) fmt.Println("Current", acc) } return acc }
This implementation takes a function fn
that takes two inputs of type T
and returns a single output of type T
. It also takes an initial value of type T
and a slice of inputs inputs
of type []T
. The function returns the final result of the reduction, which has type T
.
How do we apply the custom reduce function then?
func find_max(a, b int) int { if a > b { return a } else { return b } } max_values := myreduce(find_max,[]int{1, 3, 2, 10, 5, 6, 7}, 0) fmt.Println(max_values)
We can also use the idea behind reduce to implement a sum of a list or sequence.
func add(a, b int) int { return a + b } inputs := []int{1, 2, 3, 4, 5} result := myReduce(add, inputs,0) fmt.Println(result) // Output: 15
Filter
The filter
function takes a function and a slice of inputs, and returns a new slice of inputs that satisfy the condition defined by the function. Here’s an example implementation of filter
in Go:
// filter: sequence if condition is meet it returns those that meet the condition func myFilter(fn func(int) bool,sequence []int) []int { results := []int{} for _, v := range sequence { if fn(v) { results = append(results, v) } } return results } // support for any data type via generics func Filter[T any](fn func(T) bool, inputs []T) []T { var outputs []T for _, input := range inputs { if fn(input) { outputs = append(outputs, input) } } return outputs }
This implementation takes a function fn
that takes a single input of type T
and returns a boolean value indicating whether the input satisfies the condition. It also takes a slice of inputs inputs
of type []T
. The function returns a new slice of inputs outputs
that satisfy the condition, which has type []T
.
To use filter
, we can define a function that takes a number and returns true if the condition is meet, and then apply it to a slice of numbers:
func greaterThanZero(n int) bool { return n > 0 } func isOdd(x int) bool { return x%2 == 1 } inputs := []int{-1, 0, 1, 2, 3} outputs := Filter(greaterThanZero, inputs) fmt.Println(outputs) // Output: [1, 2, 3] odd_values := myfilter(isOdd,[]int{1, 3, 2, 10, 5, 6, 7}) fmt.Println(odd_values)
Zip
The zip
function takes two or more slices of the same length and returns a slice of tuples, where each tuple contains a value from each slice. Here’s an example implementation of zip
in Go:
func Zip[T any](slices ...[]T) [][]T { var outputs [][]T for i := range slices[0] { output := make([]T, len(slices)) for j, slice := range slices { output[j] = slice[i] } outputs = append(outputs, output) } return outputs }
This implementation takes a variable number of slices slices
of type []T
, where T
is a type parameter. The function returns a slice of tuples outputs
of type [][]T
.To use zip
, we can define two slices of strings and apply them to zip
:
input1 := []string{"hello", "world", "go"} input2 := []string{"there", "you", "are"} outputs := Zip(input1, input2) fmt.Println(outputs) // Output: [["hello" "there"], ["world" "you"], ["go" "are"]]
In this example, input1
and input2
are two slices of strings, and Zip
returns a slice of tuples, where each tuple contains a string from input1
and a string from input2
.
You can check out the video tutorial for these implementation below
In this post we’ve explored how to implement higher-order functions in Go, including map
, reduce
, filter
, and zip
. These functions are powerful tools for manipulating and transforming data , and can help you write more concise and expressive code.
Thank You For Your Attention
Jesus Saves
By Jesse E.Agbe(JCharis)