Programming paradigms
One of the biggest hurdles to understanding functional programs when coming from an imperative background is the shift in thinking. Imperative programs describe how to do something, whereas declarative programs describe what to do. Let’s sum the numbers from 1 to 10 to show this.
Imperative
#![allow(unused)] fn main() { let mut sum = 0; for i in 1..11 { sum += i; } println!("{sum}"); }
With imperative programs, we have to play compiler to see what is happening.
Here, we start with a sum
of 0
. Next, we iterate through the range from 1
to 10. Each time through the loop, we add the corresponding value in the range.
Then we print it out.
i | sum |
---|---|
1 | 1 |
2 | 3 |
3 | 6 |
4 | 10 |
5 | 15 |
6 | 21 |
7 | 28 |
8 | 36 |
9 | 45 |
10 | 55 |
This is how most of us start out programming. We learn that a program is a set of steps.
Declarative
#![allow(unused)] fn main() { println!("{}", (1..11).fold(0, |a, b| a + b)); }
Whoa! This is really different! What’s going on here? Remember that with
declarative programs we are describing what to do, rather than how to do
it. fold
is a function that
composes functions. The
name is a convention from Haskell.
Here, we are composing functions of addition (this closure: |a, b| a + b
) with
a range from 1 to 10. The 0
is the starting point, so a
is 0
at first. b
is the first element of the range, 1
. 0 + 1 = 1
is the result. So now we
fold
again, with a = 1
, b = 2
and so 1 + 2 = 3
is the next result. This
process continues until we get to the last element in the range, 10
.
a | b | result |
---|---|---|
0 | 1 | 1 |
1 | 2 | 3 |
3 | 3 | 6 |
6 | 4 | 10 |
10 | 5 | 15 |
15 | 6 | 21 |
21 | 7 | 28 |
28 | 8 | 36 |
36 | 9 | 45 |
45 | 10 | 55 |