CS101
Week 1: Functional Mindset & Immutability
Week 1: Functional Mindset & Immutability
What You’ll Learn
- Why functional programming exists
- Problems with imperative and OOP approaches
- What immutability really means
- Difference between variables and bindings
- How data flows in functional programs
Concept
Most developers start with an imperative mindset:
- You create variables
- You change them over time
- You rely on loops and mutable state
Example (imperative thinking):
balance = 100
function withdraw(amount) {
balance = balance - amount
}
Here, balance is constantly changing.
In functional programming:
- Data does not change
- Functions do not mutate state
- Instead, they return new values
Example (functional thinking in Elixir):
balance = 100
withdraw = fn balance, amount ->
balance - amount
end
new_balance = withdraw.(balance, 20)
Here:
balanceis never modifiednew_balanceis a new value
Variables vs Bindings
In Elixir, variables are not “boxes that change”.
They are bindings to values.
x = 5
x = 6
This is NOT mutation.
It means:
- First,
xis bound to5 - Then, a new binding of
xto6is created
Why This Matters in Real Systems
In real-world systems, especially concurrent ones:
- Multiple processes run at the same time
- Shared mutable state leads to:
- race conditions
- unpredictable bugs
- difficult debugging
Immutability solves this by:
- removing shared writable state
- making data predictable
- allowing safe concurrency
This is one of the core ideas behind the BEAM (Erlang VM):
Processes don’t share memory — they communicate via messages.
Examples
Example 1: Transforming Data
double = fn x -> x * 2 end
Enum.map([1, 2, 3], double)
# [2, 4, 6]
Example 2: No Mutation
list = [1, 2, 3]
new_list = [0 | list]
# list = [1, 2, 3]
# new_list = [0, 1, 2, 3]
Original data remains unchanged.
Example 3: Function Composition
[1, 2, 3]
|> Enum.map(fn x -> x * 2 end)
|> Enum.filter(fn x -> x > 2 end)
Problems
Q1
Rewrite this in functional style:
count = 0
for (i = 0; i < list.length; i++) {
if (list[i] > 0) {
count++
}
}
Q2
What will this return?
x = 10
y = x
x = 20
y
Q3
Convert this to a pure function:
total = 0
function add(x) {
total += x
}
Q4
Given a list [1, 2, 3, 4], return a new list with all elements doubled.
Q5
Explain why this is dangerous in concurrent systems:
let counter = 0
function increment() {
counter++
}
Solutions
Show Solutions
Q1
Enum.count(list, fn x -> x > 0 end)
Q2
10
Because y was bound to 10 before x was rebound.
Q3
add = fn total, x ->
total + x
end
Q4
Enum.map([1, 2, 3, 4], fn x -> x * 2 end)
Q5
Because:
- multiple threads/processes may modify
counterat the same time - leads to race conditions
- results become unpredictable
Summary
- Functional programming avoids mutable state
- Variables are bindings, not containers
- Functions transform data instead of changing it
- This model scales better for concurrent systems