The idea of this talk is presenting the Golang functional features, the pros e cons of apply functional paradigm in Golang. Do you want to improve readability and maintainability of your code using more functional paradigms? So, come on and let's have a fun time together!
2. 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 emphasizes
change of state”.
5. What this means?
● No mutable data (no side effect).
● No state (no implicit, hidden state).
Once assigned (value binding), a variable (a symbol) does not change its value
6. What this means?
● No mutable data (no side effect).
● No state (no implicit, hidden state).
Once assigned (value binding), a variable (a symbol) does not change its value.
All state is bad? No, hidden, implicit state is bad.
7. What this means?
● No mutable data (no side effect).
● No state (no implicit, hidden state).
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).
8. What this means?
● No mutable data (no side effect).
● No state (no implicit, hidden state).
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”.
9. What this means?
● No mutable data (no side effect).
● No state (no implicit, hidden state).
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”.
● Same result returned by functions called with the same inputs.
11. 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.
12. 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.
● Referential transparency: Expressions can be replaced by its values. If we call a
function with the same parameters, we know for sure the output will be the same
(there is no state anywhere that would change it).
13. 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.
● Referential transparency: Expressions can be replaced by its values. If we call a
function with the same parameters, we know for sure the output will be the same
(there is no state anywhere that would change it).
There is a reason for which Einstein defined insanity as "doing the same thing over
and over again and expecting different results".
15. Advantages enabled by referential transparency
● Memoization
○ Cache results for previous function calls.
16. Advantages enabled by referential transparency
● Memoization
○ Cache results for previous function calls.
● Idempotence
○ Same results regardless how many times you call a function.
17. Advantages enabled by referential transparency
● Memoization
○ Cache results for previous function calls.
● Idempotence
○ Same results regardless how many times you call a function.
● Modularization
○ We have no state that pervades the whole code, so we build our project with
small, black boxes that we tie together, so it promotes bottom-up
programming.
18. Advantages enabled by referential transparency
● Memoization
○ Cache results for previous function calls.
● Idempotence
○ Same results regardless how many times you call a function.
● Modularization
○ We have no state that pervades the whole code, so we build our project with
small, black boxes that we tie together, so it promotes bottom-up
programming.
● Ease of debugging
○ Functions are isolated, they only depend on their input and their output, so
they are very easy to debug.
19. Advantages enabled by referential transparency
● Parallelization
○ Functions calls are independent.
○ We can parallelize in different process/CPUs/computers/…
20. Advantages enabled by referential transparency
● Parallelization
○ Functions calls are independent.
○ We can parallelize in different process/CPUs/computers/…
result = func1(a, b) + func2(a, c)
21. Advantages enabled by referential transparency
● Parallelization
○ Functions calls are independent.
○ We can parallelize in different process/CPUs/computers/…
We can execute func1 and func2 in parallel because an won’t be modified.
result = func1(a, b) + func2(a, c)
22. Advantages enabled by referential transparency
● Concurrence
○ With no shared data, concurrence gets a lot simpler:
■ No semaphores.
■ No monitors.
■ No locks.
■ No race-conditions.
■ No dead-locks.
23. Ok so let's play a little with functional programming?
29. Don’t Update, Create - Maps
ages := map[string]int{"John": 30}
ages["Mary"] = 28
ages // {"John": 30, "Mary": 28}
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[string]int, len(mapA) + len(mapB))
for k, v := range mapA {
allAges[k] = v
}
for k, v := range mapB {
allAges[k] = v
}
return allAges
}
allAges := mergeMaps(ages1, ages2)
30. 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(name string) string {
return "Hello " + name
}
caller(f)
31. Higher Order Functions - Map
// As Golang do not have a builtin Map implementation, it is possible use this one
// https://github.com/yanatan16/itertools/blob/master/itertools.go
mapper := func (i interface{}) interface{} {
return strings.ToUpper(i.(string))
}
Map(mapper, New("functional", "go"))
//["FUNCTIONAL", "GO"]
32. Higher Order Functions - Filter
// As Golang do not have a builtin Filter implementation, it is possible use this one
// https://github.com/yanatan16/itertools/blob/master/itertools.go
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]
33. Higher Order Functions - Reduce
// As Golang do not have a builtin Reduce implementation, it is possible use this one
// https://github.com/yanatan16/itertools/blob/master/itertools.go
acumullator := func (memo interface{}, el interface{}) interface{} {
return len(memo.(string)) + len(el.(string))
}
Reduce(New("functional", "go"), acumullator, string).(uint64)
// result 12
34. Higher Order Functions - 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 15
add_7(10) // result 17
35. 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
}
37. Not Well Supported - 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.
38. Not Well Supported - 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.
39. Not Well Supported - 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.
40. Not Well Supported - 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.
● If every recursion created an stack, it would blow up very soon.
41. Not Well Supported - 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.
● If every recursion created an stack, it would blow up very soon.
● Tail-call optimization (TCO) avoids creating a new stack when the last call in a
recursion is the function itself.
42. Not Well Supported - 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.
● If every recursion created an stack, it would blow up very soon.
● Tail-call optimization (TCO) avoids creating a new stack when the last call in a
recursion is the function itself.
● TCO is not implemented in Golang.
43. Not Well Supported - 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.
● If every recursion created an stack, it would blow up very soon.
● Tail-call optimization (TCO) avoids creating a new stack when the last call in a
recursion is the function itself.
● TCO is not implemented in Golang.
● Unfortunately following recursion style in Golang has it’s own tax: Performance.
44. 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)
}
45. Solving Golang Lack of TCO(Tail Call Optimization)
// 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
}
47. Not Well Supported - Eager vs Lazy Evaluation
● Eager evaluation: expressions are calculated at the moment that variables is
assined, function called...
48. Not Well Supported - Eager vs Lazy Evaluation
● Eager evaluation: expressions are calculated at the moment that variables is
assined, function called...
● Lazy evaluation: delays the evaluation of the expression until it is needed.
○ Memory efficient: no memory used to store complete structures.
○ CPU efficient: no need to calculate the complete result before returning.
○ Laziness is not a requisite for FP, but it is a strategy that fits nicely on
the paradigm(Haskell).
49. Not Well Supported - Eager vs Lazy Evaluation
● Eager evaluation: expressions are calculated at the moment that variables is
assined, function called...
● Lazy evaluation: delays the evaluation of the expression until it is needed.
○ Memory efficient: no memory used to store complete structures.
○ CPU efficient: no need to calculate the complete result before returning.
○ Laziness is not a requisite for FP, but it is a strategy that fits nicely on
the paradigm(Haskell).
Golang uses eager evaluation (but short-circuits && or ||).
50. Not Well Supported - Eager vs Lazy Evaluation
● Eager evaluation: expressions are calculated at the moment that variables is
assined, function called...
● Lazy evaluation: delays the evaluation of the expression until it is needed.
○ Memory efficient: no memory used to store complete structures.
○ CPU efficient: no need to calculate the complete result before returning.
○ Laziness is not a requisite for FP, but it is a strategy that fits nicely on
the paradigm(Haskell).
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.
51. Not Well Supported - Eager vs Lazy Evaluation
● Eager evaluation: expressions are calculated at the moment that variables is
assined, function called...
● Lazy evaluation: delays the evaluation of the expression until it is needed.
○ Memory efficient: no memory used to store complete structures.
○ CPU efficient: no need to calculate the complete result before returning.
○ Laziness is not a requisite for FP, but it is a strategy that fits nicely on
the paradigm(Haskell).
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.
52. Example of Golang generator using goroutine and channel
func fib(n int) chan int {
c := make(chan int) // create a channel
go func() { // keyword "go" starts a goroutine
x, y := 0, 1
for k := 0; k < n; k++ {
c <- x // send value x to the channel c
x, y = y, x + y // swap
}
close(c) // close(c) sets the status of the channel c to false
// indicates that the channel is now empty
}()
return c
}
53. Example of Golang generator using goroutine and channel
func main() {
fc := fib(5)
fmt.Println("The first 5 Fibonacci numbers:", <-fc, <-fc, <-fc, <-fc, <-fc)
// The first 5 Fibonacci numbers: 0 1 1 2 3
// trap the possible end of supply this way
fc2 := fib(5)
fmt.Println("The next 5 Fibonacci numbers:")
for k := 0; k < 6; k++ {
val, status := <-fc2
if status == false {
fmt.Println("channel empty")
break
}
fmt.Printf("%d ", val)
}
//The next 12 Fibonacci numbers:
//0 1 1 2 3 channel empty
}
56. A Practical 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
}
57. A Practical Example
Exercise: "What's the sum of the first 10 natural number whose square value is
divisible by 5?"
Imperative: 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) //275
func main() {
n, numElements, s := 1, 0, 0
for numElements < 10 {
if n * n % 5 == 0 {
s += n
numElements++
}
n++
}
fmt.Println(s) //275
}
59. Conclusion
● As you can tell, Golang helps you write in functional style but it doesn’t force
you to it.
60. Conclusion
● As you can tell, Golang helps you write in functional style but it doesn’t force
you to it.
● Writing in functional style enhances your code and makes it more self documented.
Actually it will make it more thread-safe also.
61. Conclusion
● As you can tell, Golang helps you write in functional style but it doesn’t force
you to it.
● Writing in functional style enhances your code and makes it more self documented.
Actually it will make it more thread-safe also.
● The main support for FP in Golang comes from the use of closures, iterators and
generators.
62. Conclusion
● As you can tell, Golang helps you write in functional style but it doesn’t force
you to it.
● Writing in functional style enhances your code and makes it more self documented.
Actually it will make it more thread-safe also.
● The main support for FP in Golang comes from the use of closures, iterators and
generators.
● Golang still lack an important aspect of FP: Map, Filter, Reduce, Immutable and
Generic types, Pattern Matching and Tails Recursion.
63. Conclusion
● As you can tell, Golang helps you write in functional style but it doesn’t force
you to it.
● Writing in functional style enhances your code and makes it more self documented.
Actually it will make it more thread-safe also.
● The main support for FP in Golang comes from the use of closures, iterators and
generators.
● Golang still lack an important aspect of FP: Map, Filter, Reduce, Immutable and
Generic types, Pattern Matching and Tails Recursion.
● There should be more work on tail recursion optimization, to encourage developers
to use recursion.
64. The last advice
Learn at least one functional language, it will open your mind to a new paradigm
becoming you a better programmer.
65. 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
● ML (Standard ML, Objective Caml, ...)
● Scheme
● Erlang
● Scala
● Closure
● F#