Functional Programming

I came across this post on X, which has generated some debate about functional programming.
So is functional programming more work than imperative programming? And is it worth its trouble?
We are going to find out in this text!
What is functional programming?
Functional programming is a paradigm in which we try to solve problems with pure mathematical functions.
It comes from an old model of computation called lambda calculus which was created by Alonzo Church (who was a professor of Alan Turing, the father of computer science and creator of the Turing machine [the computer]).
To fully understand functional programming, we should have an idea of imperative programming.
Imperative programming
Imperative programming is the programming you learn when first learning to program. You have a list of actions and the computer follows them, step by step.
Here is a good example to compare both of them: Fibonacci. Fibonacci is the sequence we get when summing the 2 previous values, starting with 0 and 1. So 0+1 = 1, 1+1 = 2, 2+1 = 3, 3+2 = 5,… and so forth. We can represent this as:
Imperative example
Functional example
Another classical example is BFS (Breadth First Search) and DFS (Depth First Search), which are ways to search a graph.
BFS
DFS
As we can see, they can have real practical differences in the real world. BFS uses a queue (FIFO) and explores level by level, while DFS uses a stack (LIFO) and goes deep before backtracking. In imperative code, you typically manage these data structures with loops and mutation. In functional programming, recursion naturally implements DFS, while BFS can be expressed with immutable queues.
What is the problem with imperative methods?
The main problem with imperative methods is that we have to mutate state to achieve things, which makes it hard to reason about program behavior, especially in concurrent scenarios.
And maintaining state by mutating it can get tricky, and we start having well-known problems, like race conditions.
What is the problem with functional programming?
Well, I guess defining problems mathematically and solving them that way can be hard. But is it worth it?
Yes, you will have a mathematical solution for a problem (math always works!), so you will not have to deal with weird corner cases you did not first think of with imperative methods.
Practical example
A great example I like is state machines. State machines are ways to represent states (they are out of the scope of this text, but they are great tools to manage state).
Let’s look at a simple on/off switch state machine:
stateDiagram-v2
[*] --> OFF
OFF --> ON: TurnOn
ON --> OFF: TurnOff
Now, we can implement this same state machine in both imperative and functional ways:
Imperative Example
package main
import (
"fmt"
"sync"
)
type Switch struct {
mu sync.Mutex
on bool
}
func (s *Switch) TurnOn() { s.mu.Lock(); defer s.mu.Unlock(); s.on = true }
func (s *Switch) TurnOff() { s.mu.Lock(); defer s.mu.Unlock(); s.on = false }
func (s *Switch) Toggle() { s.mu.Lock(); defer s.mu.Unlock(); s.on = !s.on }
func (s *Switch) IsOn() bool { s.mu.Lock(); defer s.mu.Unlock(); return s.on }
func main() {
var sw Switch
sw.TurnOn()
fmt.Println("on?", sw.IsOn()) // true
sw.Toggle()
fmt.Println("on?", sw.IsOn()) // false
}
Functional Example
package main
import "fmt"
type event int
const (
evTurnOn event = iota
evTurnOff
evToggle
evQuit
)
type fsm struct {
state stateFn
}
type stateFn func(*fsm, event) stateFn
func stateOff(m *fsm, ev event) stateFn {
switch ev {
case evTurnOn, evToggle:
fmt.Println("OFF → ON")
return stateOn
case evTurnOff:
fmt.Println("OFF (no-op)")
return stateOff
case evQuit:
fmt.Println("quit from OFF")
return nil
default:
return stateOff
}
}
func stateOn(m *fsm, ev event) stateFn {
switch ev {
case evTurnOff, evToggle:
fmt.Println("ON → OFF")
return stateOff
case evTurnOn:
fmt.Println("ON (no-op)")
return stateOn
case evQuit:
fmt.Println("quit from ON")
return nil
default:
return stateOn
}
}
func (m *fsm) Step(ev event) bool { // returns false when machine stops
if m.state == nil {
return false
}
m.state = m.state(m, ev)
return m.state != nil
}
func main() {
m := &fsm{state: stateOff}
for _, ev := range []event{evToggle, evTurnOn, evTurnOff, evQuit} {
if cont := m.Step(ev); !cont {
break
}
}
}
The functional state machine follows Rob Pike’s pattern, which he explains in a lecture he gave about Go’s lexical scanning.
At first glance, the functional approach looks more complicated. However, it has a crucial advantage: we never mutate state, and as complexity grows this is priceless!
Consider a more complex system, like the poker game I’m building: https://github.com/vctt94/pokerbisonrelay
This game has many states: player states, table states, game states. With imperative code, you’d maintain these by spreading if/else checks and toggling flags around the code, which quickly becomes hell.
The functional state machine pattern keeps all state transitions in one place, explicit and predictable.
Poker State Machine
%% Game FSM — complete state flow with all intermediate states
stateDiagram-v2
[*] --> NEW_HAND_DEALING
NEW_HAND_DEALING: Wait for evStartHand
NEW_HAND_DEALING --> PRE_DEAL: evStartHand
PRE_DEAL: Setup positions, post blinds, init hand
PRE_DEAL --> DEAL: automatic
DEAL: Deal hole cards to players
DEAL --> BLINDS: automatic
BLINDS: Set current player to act
BLINDS --> PRE_FLOP: automatic
PRE_FLOP: Pre-flop betting round
PRE_FLOP --> FLOP: evAdvance (betting complete)
PRE_FLOP --> SHOWDOWN: evGotoShowdown (fold/win)
FLOP: Deal flop (3 cards), betting round
FLOP --> TURN: evAdvance (betting complete)
FLOP --> SHOWDOWN: evGotoShowdown (fold/win)
TURN: Deal turn (4th card), betting round
TURN --> RIVER: evAdvance (betting complete)
TURN --> SHOWDOWN: evGotoShowdown (fold/win)
RIVER: Deal river (5th card), betting round
RIVER --> SHOWDOWN: evAdvance / evGotoShowdown
SHOWDOWN: Evaluate hands, distribute pots
SHOWDOWN --> PRE_DEAL: evStartHand (next hand)
SHOWDOWN --> END: no evStartHand
END: Terminal state
END --> [*]
It is just math!
Conclusion
So yeah, functional programming sometimes does require more of a thinking process, but in the end it can be totally worth it.
Thanks for reading this far!