Immutability is kind of a fashion trend (see here), but for beginners it may be challenging to understand the concepts. How can a program do anything without changing values? I´m not a specialist in FP nor an advocat for this concept, but hopefully I can explain some basics.
Immutable means "unchangeable", and languages like Haskell do indeed know only constants, no variables. If you want to mutate a value, you need to create a copy the new value, which is derived from the old one. The old value never changes. But how do you write loops then?
Let see a classic example in JS:
let i
for (i=0; i<5; i++)
console.log("the value is "+i)
The same code line is executed multiple time while incrementing the variable i, i is "mutated". What´s wrong about mutation? Honestly, nothing. Loops are a very basic operation of most programming languages and they are easy to implement. In a fictive assembler language loops would look like this:
set 0 -> ADR_I
LBL1 call console.log(ADR_I)
INC ADR_I
COMPARE ADR_I, 5
JMP_IFNOT --> LBL1
They all work by mutating variables (or adresses). So, how can we build a loop without variables? One recommended way in haskell are recursions. We can use this concept in Javascript too:
function loop(n) {
if (n > 1)
loop(n - 1)
console.log("the value is "+n)
}
Loop calls itself recursively until the condition is reached. After that, the log starts with the lowest value, so we get
loop(5)
the value is 1
the value is 2
the value is 3
the value is 4
the value is 5
Does this have any advantage for the programmer? If you are a "functional programmer", your are possibly happy to have a solution without mutation. But do you really not mutate anything? Behind the scenes, you use the stack as your memory. This is what happens:
loop(5) -->
loop(4) -->
loop(3) -->
loop(2) -->
loop(1) -->
console.log("the value is "+1)
console.log("the value is "+2)
console.log("the value is "+3)
console.log("the value is "+4)
console.log("the value is "+5)
This is ok, if your loop is small, but what, if you need to count to a large number? Very quickly your stack will overflow, as this is limited to a fixed size. Anyway, storing a return address for every loop circle is far from efficient. Surplus to your loop code you need the complete stack handling, which will consume memory and time.
From a theoretical point, FP has some advantages. But in some cases it comes with an overhead that can be fairly expensive. Debugging recursive code by the way is far from convenient. Unlike a standard loop, where you can simply watch the variable value of the iterator, you need to watch the stack pointer. The loop above will return 5 times to the same function, but the 6th time it returns to the caller. Debugging recursive code is often very confusing.
So, like any paradigm, using FP comes not for free. You have to learn not only the language, but also know the concepts and algorithms to use it properly. It is often mentioned, that there is no solution that fits all needs. A paradigm might fit best for a certain class of problems, while it is not ideal for another. You are best suited if you know different paradigms and can choose, what fit´s best.
Happy coding!
Top comments (6)
Extreme functional programming has some significant drawbacks compared to its advantages, but the concept of not modifying the input seems quite useful.
If the guarantee that every function does not modify the input is maintained, in most cases, logic can be handled with shallow copies.
Most people think, FP and OOP exclude each other, but this is not true. There is no limitation to build parts of your code following the principles of FP and call this functions from within class methods.
Even inside a class it can be most beneficial to limit the number of side effects and use mainly pure functions. This is already good programming style. But using ONLY functions may make things more complilcated then necessary.
I guess a good and healthy combination of both styles should be preferred. OOP really benefits FP and vice versa.
Well said.
Thank you Eckehard ,
Look at this :
Trampoline
TCO
JavaScriptCore (Safari, Bun) is the only engine that implements TCO
Chrome (V8) has marked TCO as No longer Pursuing.
Tail call optimization in ECMAScript 6