Function: The Savior of Scope. The Story Of Immutability And Declarative.

Acid Coder
5 min readJun 22, 2023

You likely heard of the term Functional Programming, that it is declarative, immutable, pure function(no side effect) and it uses recursion over loop.

Immutable, declarative, pure function and recursion, we repeat these words every time we talk about functional programming

But why?

Today we are going to examine how immutability, declarative, pure function and recursion are related to each other and why we cant mention one without mentioning the rest.

Immutability

First let us review why we want to avoid mutable variables if possible by comparing it to immutable variables

Possible mistake with mutable variables:

let temperature = 30
let distance = 3000
//…others code
temperature = 2000 // mistake, you should change the distance!
// …others code
displayTemperature(temperature) // mistake 1
displayTemperature(distance) // mistake 2

Possible mistake with immutable variables:

const temperature = 30
const distance = 3000
//…others code
temperature = 2000 // compiler will complain!!
// …others code
displayTemperature(temperature) // no problem
displayTemperature(distance) // mistake 1

both mutable and immutable suffer from the same mistake: reading them at wrong places

This is not something compiler can take care of, you need to write tests to mitigate mistakes like this.

However, mutable variables has one extra point of failure: assigning value to a wrong variable. It can be very hard to debug because value assignment can happen in anywhere.

We can mitigate wrong variable value assignment by writing tests too, but writing tests take away our precious engineering time. Mistake like this can be prevented by using immutable variables.

Prevention is better than cure.

Declarative Code

We heard of “imperative” and “declarative” a lot, but how to tell which code is imperative and which code is declarative?

The keyword of writing a declarative code is “expression”.

If we can express a piece of code as a value, then it is an expression, example:

can be expressed as a value:

condition ? 1 : 2

cannot be expressed as a value:

if(condition){
1
} else {
2
}

Simple enough, but how is this related to immutability?

Meaningful Value

A value has to have a purpose, else it would be pointless to create the value in the first place

so what is the purpose of a value?

Recall what computer do: accept an input, process it, and output the result

A value is only meaningful if we read it(turn the output into another input)

Turn an expression into an input:

By assigning it to a variable, preferably immutable variable:

const input = condition ? 1 : 2
doSomething(input)

By reading the expression directly:

doSomething(condition ? 1 : 2)

Turn non-expression code into an input:

By mutable variable:

let input = null
if(condition){
input = 1
} else {
input = 2
}
doSomething(input)

This is bad, because we need to rely on mutable variable to escape `if` scope

By `var` or global variable:

if(condition){
var input = 1
} else {
input = 2
}
doSomething(input)
if(condition){
input = 1
} else {
input = 2
}

doSomething(input)

I believe I don’t need to explains why they are terrible ideas.

By running the side effect in the `if` scope:

if(condition){
doSomething(1)
} else {
doSomething(2)
}

`doSomething` is a side effect, it has to be one. Because in the end it either end up sending the data somewhere else or storing the data in a database or display the data on a screen(become an input of our brain).

If not, then `doSomething` is meaningless, our `if` statement is meaningless.

We coupled `doSomething` into the `if` statement. Which mean it is not possible to test the `if` statement without testing the side effect `doSomething`.

Scope `{}`

Notice that scopes `{}` from statements like `if`, `try catch`, `switch`, `for`, `while`… are the reason why we need mutable variable to access values generated in scopes.

Scope limit the access to variables, this a good mechanism but we need to sacrifice immutability.

The more we nest our scope, the more we rely on mutable variables.

Which mean scope nesting not only affect our code readability, it also affects our code correctness.

Bad readability produces bugs, bad readability and mutable variables produce even more bugs!

So should we give up on scope?

No, in fact we can create immutable variable from scopes.

Function, The Savior of Scope

Not only function scope can limit the accessibility of variables, it allow us to read a value from scopes without creating any mutable variable.

From the same example:

const getResult = (condition) => {
if(condition){
return 1
} else {
return 2
}
}
const input = getResult(false)
doSomething(input)

checking the list:
✅ scope
✅ immutable
✅ pure function

However this does not work with loop scope, loop always end up with mutable variable or side effect.


const getSumUpToN= (n) => {
let sum = 0 // mutation
for(let i = 1; i <= n ;i ++){ // mutation
sum += i
}
return sum
}
const input = getSumUpToN(5)

Recursion

This is where recursion enter the picture. We can use recursion to preserve the immutability.

Function is the key to make recursion possible.

const getSumUpToN= (n) => {
return n <= 0 ? 0 : n + getSumUpToN(n — 1)
}
const input = getSumUpToN(5)
doSomething(input)

Now we understand why functional programming use recursion over loop, it is because **recursion can express loop as an value and avoid mutable variables**

We also understand why functional programming is functional, it is because function is an expression and it make recursion possible.

Summary

1. The foundation of functional programming is expression(value)
2. The tool is function. Function can turn everything into expressions by returning values from scopes and use recursion over loop.
3. The result is a flattened code where it is unnecessary to limit the accessibility of variables
4. The main benefits are immutability and pure function(improve code correctness), the extra benefit is declarative(improve readability).

Final Thoughts

I believe the point of functional programming is to remove the need of scope.

We need scope to limit accessibilities of variables in order to code safer, but at the same time scope either introduce mutable variable or couple side effects into statements.

By using function, we can maintain immutability and decouple from side effect while limiting accessibilities of variables.

I hope this article give you a clear idea of how to write a basic functional code and why you need to write one.

Stay vigilant fellow programmers!

--

--

Acid Coder

Typescript Zombie. Youtube Pikachu On Acid. (Unrelated to programming but by watching it you become a good developer overnight)