Functional Go

Geison
Geison
Sep 28, 2015 · 6 min read

Functional Programming by Wikipidia:

“Functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids state and mutable data”. In other words, functional programming promotes code with no side effects, no change of value in variables. It oposes to imperative programming, which enfatizes change of state”.


What this means?

  • No mutable data (no side effect).

Once assigned (value binding), a variable (a symbol) does not change its value.

All state is bad? No, hidden, implicit state is bad.

Functional programming do not eliminate state, it just make it visible and explicit (at least when programmers want it to be).

  • Functions are pure functions in the mathematical sense: their output depend only in their inputs, there is not “environment”.

What are the advantages?

  • Cleaner code: “variables” are not modified once defined, so we don’t have to follow the change of state to comprehend what a function, a, method, a class, a whole project works.

There is a reason for which Einstein defined insanity as “doing the same thing over and over again and expecting different results”.


Advantages enabled by referential transparence

  • Memoization
result = func1(a, b) + func2(a, c)

We can execute func1 and func2 in paralell because a won’t be modified.

  • Concurrence
  1. With no shared data, concurrence gets a lot simpler:

Golang is a multi paradigm programming language. As a Golang programmer why uses functional programming?

Golang is not a functional language but have a lot of features that enables us to applies functional principles in the development, turning our code more elegant, concise, maintanable, easier to understand and test.


Don’t Update, Create — String

Wrong

name := “Geison”name := name + “ Flores”

Right

const firstname = “Geison”const lasname = “Flores”const name = firstname + “ “ + lastname

Don’t Update, Create — Arrays

Wrong

years := [4]int{2001, 2002}years[2] = 2003years[3] = 2004years // [2001, 2002, 2003, 2004]

Right

years := [2]int{2001, 2001}allYears := append(years, 2003, [2]int{2004, 2005}

Don’t Update, Create — Maps

Wrong

ages := map[string]int{“John”: 30}ages[“Mary”] = 28ages // {‘John’: 30, ‘Mary’: 28}

Right

ages1 := map[string]int{“John”: 30}ages2 := map[string]int{“Mary”: 28}func mergeMaps(mapA, mapB map[string]int) map[string]int {    allAges := make(map[K]V, len(ages1) + len(ages2))    for k, v := range mapA {        allAges[k] = v    }    for k, v := range mapB {        allAges[k] = v    }    return allAges}allAges := mergeMaps(ages1, ages2)

Higher Order Functions

Functions and methods are first-class objects in Golang, so if you want to pass a function to another function, you can just treat it as any other object.

func caller(f func(string) string) {    result := f(“David”)    fmt.Println(result)}f := func(s name) string {    return “Hello “ + name}caller(f)

As Golang don`t have a builtin Map implementation, it is possible use this one below:

Map

mapper := func (i interface{}) interface{} {    return strings.ToUpper(i.(string))}Map(mapper, New(“milu”, “rantanplan”))//[“MILU”, “RANTANPLAN”]

Filter

pred := func (i interface{}) bool {    return i.(uint64) > 5}Filter(pred, Uint64(1,2,3,4,5,6,7,8,9,10))//[6, 7, 8, 9, 10]

Reduce

acumullator := func (memo interface{}, el interface{}) interface{} {    return len(memo.(string)) + len(el.(string))}Reduce(New(“milu”, “rantanplan”), acumullator, string).(uint64)// result 14

Closure

func add_x(x int) func() int {    return func(y int) int { // anonymous function        return x + y    }}add_5 := add_x(5)add_7 := add_x(7)add_5(10) // result 15add_7(10) // result 17

Currying and Partial Functions

Higher-order functions enable Currying, which the ability to take a function that accepts n parameters and turns it into a composition of n functions each of them take 1 parameter. A direct use of currying is the Partial Functions where if you have a function that accepts n parameters then you can generate from it one of more functions with some parameter values already filled in.

func plus(x, y int) int {    return x + y}func partialPlus(x int) func(int) int {    return func(y int) int {        return plus(x, y)    }}func main() {    plus_one := partialPlus(1)    fmt.Println(plus_one(5)) //prints 6}

Eager vs Lazy Evaluation

  • Eager evaluation: expressions are calculated at the moment that variables is assined, function called…

Golang uses eager evaluation (but short-circuits && or ||).

Golang channels and goroutines enable the creation of generators that could be a way to have lazy evaluation.

Golang arrays are not lazy, use channels and goroutines to emulate a generator when necessary.


Recursion

Looping by calling a function from within itself. When you don’t have access to mutable data, recursion is used to build up and chain data construction. This is because looping is not a functional concept, as it requires variables to be passed around to store the state of the loop at a given time.

  • Purely functional languages have no imperative for-loops, so they use recursion a lot.

Solving Golang Lack of TCO(Tail Call Optimization)

The functional solution have problems with big values

func fibonacciRecursive(n int) int {    if n <= 1 {        return n    }    return n * fibonacciRecursive(n — 1)}

The iterative solution works perfectly with large values

func fibonacci(n int) int {    current, prev := 0, 1    for i := 0; i < n; i++ {        current, prev = current + prev, current    }    return current}

FP in OOP?

It is possible do FP in OOP? Yes it is!

  • OOP is orthogonal to FP.

A Pratical Example

Exercise: “What’s the sum of the first 10 natural number whose square value is divisible by 5?”

Imperative

func main() {    n, numElements, s := 1, 0, 0    for numElements < 10 {        if n * n % 5 == 0 {            s += n            numElements++        }        n++    }    fmt.Println(s) //275}

Functional

sum := func (memo interface{}, el interface{}) interface{} {    return memo.(float64) + el.(float64)}pred := func (i interface{}) bool {    return (i.(uint64) * i.(uint64)) % 5 == 0}createValues := func () []int {
values := make([]int, 100)
for num := 1; num <= 100; num++ { values = append(values, num) }
return values
}Reduce(Filter(pred, createValues), sum, uint64).(uint64)

The last advice

Learn at least one functional language, it will open your mind to a new paradigm becoming you a better programmer.

Some Functional Languages:

  • Haskell

Conclusion

  • As you can tell, Golang helps you write in functional style but it doesn’t force you to it.

Geison

Written by

Geison

Software Craftsman, traveller, dog trainer and Husband.