Golang functions

From wikinotes

Golang has first class functions, you can pass functions as parameters.

Function Signatures

Function with arguments, return value

//        (param  type) (return-type)
func greet(name string) string {
    return "Hello, " + name
}

Multiple return values

// when multiple return values present, surround with brackets
func find(id int) (string, int) {
    // ...
}

// optionally, return values can be named
// (this has no implications for caller, it's simply documentation)
func find(id int) (name string, age int) {
    // 'values' is array ...
}

Combine params with same type

// params 'firstname', 'lastname' both have type 'string'
func register(firstname, lastname string) {
    // ...
}

Param values vs pointers

// if 'name' param changes in func, does not change outside
func register immutable(name string) { ... }

// if 'name' param changes in func, modifies value in memory
// (more efficient, does not copy data to new memory location)
func register_mutable(*name string) { ... }

Variadic parameters (variable number of params)

func sum(names ...string) {
    // 'names' is a slice of 'string's
    new_names = append([]int{alex}, names...) // prepend to names slice
}

Returning pointers

// even if variables are declared on the stack,
// you can safely return a pointer to them
// (as lomg as your return value is a pointer)
func DoThing() *int {
    var foo *int = &123
    return foo
}

Optional parameters

// all parameters are optional!
func DoThing(a string, b int) {
    fmt.Printf("'%s', '%s'", a, b)
    return
}

DoThing()  // "'', ''"

Default parameter values

// no syntactic sugar for defaults, but can handle inside function
func SayHello(name string) {
    if name == "" {
        name = "guest"
    }
    fmt.Printf("Hello %s", name)
}

Functions as Parameters (TESTME!)

func Visit(msg string, visitor func(string) string) string {
    return visitor(msg)
}

func Visitor(msg string) string {
    fmt.Println(msg)
}

// you can pass in a function
Visit("abc", Visitor)

// you can also define functions ad-hoc
Visit("abc", func(msg string) string {
})

Generics

Generic functions can accept params for a range of types.

// this method accepts either an int/float for both 'a' and 'b' params
// (we define value for 'NUM', then reuse it)
func Sum[NUM int | float64](a NUM, b NUM) NUM {
    return a + b
}

Deferred functions

Defer waits until a function is just about to exit (even on failure).
This is similar to a try/finally block in other languages.
Docs describe it as useful for releasing a mutex, for example.

func WriteFile(filepath string) (success int) {
    fd, err := os.Open(filepath)
    defer fd.Close()  // run before function closes

    // ... other code ...
}
  • Deferred functions are executed in the order of last-in-first-out (LIFO).
  • Panics evaluate after deferred functions

Variables passed to a defferred function retain their value at the time the function call was made.

func main() {
    a := "start"
    defer fmt.Println(a)
    b := "end"
}
// "start"   <-- 'a' at time of defer was 'start'

You may want to handle errors from deferred functions.
you can do this with a closure, but you'll need to return an array of errors.

func DoThing() (result string, errs []error) {  // <-- errs !MUST! be declared here for deferred to be caught
    defer func() {
        err = pipe.Close()
        if err != nil {
            errs = append(errs, err)
        }
    }()

    return "success", errs
}

Function Overloading

Not supported in go.

Anonymous Functions

You can define anynymous functions in go

foo = func() { ... }
defer func() { ... }
go func() { ... }

You can also use the func generic type to type these anonymous functions.

var sum func(int, int) int
sum = func(a, b, int) int {
    return a + b
}

Recursion / Tail Call Optimization

TODO:

Why wouldn't tailCount also continuously increase the stack? It feels like this would be even larger than regular recursion..

Go does not do tail call optimization.

You can force this optimization by executing your recursive function call in a second function.
See https://medium.com/@meeusdylan/tail-recursion-in-go-fb5cf69a0f26

func tailCount(i int) {
    return count(i)
}

func count(i int) {
    if i == 20 {
        return i
    }
    return tailCount(i)
}