Functions
How functions work in golang
Last updated
How functions work in golang
Last updated
We've already seen various functions at this point, beginning with the main
function. But on this page we will explore various functions including simple functions, functions that return multiple values, anonymous (no-name) functions, variadic functions, and closures.
Functions can be assigned to variables, passed as arguments to functions or returned from functions. This makes the language more flexible. A function declaration begins with the func
keyword. The body is wrapped around curly brackets {}
and the function can have parameters or chose to omit them.
In the example below, we will define a simple function that solves a quadratic equation using the quadratic formula:
package main
import (
"fmt"
"math"
)
// define a function called solve_quadratic that takes three float values
// and returns a float as the answer
func solve_quadratic(a float64, b float64, c float64) float64 {
bb := b * b
quotient := (-1 * b) + math.Sqrt((bb - (4 * a * c)))
divisor := 2 * a
ans := quotient / divisor
return ans
}
func main() {
a := 1.0
b := -3.0
c := -4.0
// call the solve_quadratic function with some values
ans1 := solve_quadratic(a, b, c)
fmt.Println("Ans: ", ans1)
// call the function again with different values
ans2 := solve_quadratic(-10.5, 3.99, -8.475)
}
The example above only returned one value. However, we can modify the function to return two (or more values) as follows
package main
import "math"
// set the return type of the values
func solve_quadratic(a, b, c float64) (float64, float64) {
bb := b * b
q1 := (-1 * b) + math.Sqrt((bb - (4 * a * c)))
q2 := (-1 * b) - math.Sqrt((bb - (4 * a * c)))
divisor := 2 * a
x1 := q1 / divisor
x2 := q2 / divisor
// return both x1 and x2
return x1, x2
}
func main() {
// we can later call the function as follows
a, b := solve_quadratic(1.0, -3.0, -4.0)
// if we are only interested in one value, we can use the underscore
// to ignore the other. For exmaple
_, b := solve_quadratic(2.0, -3.5, 5.0)
}
Anonymous functions do not have a name. They can be useful if/when we need to apply a function to some values, but we don't necessarily want to write out a named function for the task. Here is an example:
package main
import "fmt"
func main() {
mul := func(a, b, c int) int {
return a * b * c
}(19, 11, 7)
fmt.Println("19 * 11 * 7 = ", mul)
// prints: 19 * 11 * 7 = 1463
}
A variadic function is one that can be called with any number of parameters. The function also accepts an arbitrary number of arguments. We've already seen a function of this type, the fmt.Println()
function, which can take an arbitrary number of arguments. Here is how we can define our own in golang
package main
import "fmt"
// adds all the numbers passed as parameters and returns a total
func adder(nums ...int) int {
fmt.Print(nums, " ")
sum := 0
for _, num := range nums {
sum += num
}
return sum
}
func main() {
fmt.Println(adder(1, 2, 3, 4)) // prints: 10
fmt.Println(adder(99, 13)) // prints: 112
scores := []int{4, 5, 19, 6, 2, 11}
fmt.Println(adder(scores...)) // prints: 47
}
If you already have multiple args in a slice (as in the case of line 18 above), we can apply them to a variadic function using func(slice...)
. We will cover slices in detail in the Slices section
The last thing related to functions we would look at is closures. A closure is a concept in programming languages where a function returns another function. In other words, a closure gives you access to an outer function's scope (lexicological environment) from an inner function. (See this Wikipedia article to get an in-depth definition of closures). Here is how golang closures work:
package main
import "fmt"
// a simple program that takes a seed, and increments the seed
// number by one every time the closure function is called
func incrementor(init int) func() int {
i := init
return func() int {
i++
return i
}
}
func main() {
randSequence := incrementor(23)
fmt.Println(randSequence())
fmt.Println(randSequence())
fmt.Println(randSequence())
anotherSequence := incrementor(2412)
fmt.Println(randSequence())
fmt.Println(randSequence())
}
$ go run closure_function.go
24
25
26
2413
2414
Functions in golang are very flexible and allow for extensibility as shown above. In golang, we can define our own type for a function, pass functions as arguments into other functions, and return functions as closures. This level of flexibility in functions is known as first-class functions. To learn more about first-class functions and further ideas in golang, see: https://golangbot.com/first-class-functions/