Functional vs Imperative Programming, for the umpteenth time

One of the biggest problems I have learning React.js is its functional nature, instead of imperative programming I've always known. It is a different way of thinking about how to solve a problem... i.e. a new paradigm.

This is NOT an intro the functional programming, but more about my personal struggles to understand functional programming. For a good intro to functional programming I suggest this Russ Olsen video presented at GOTO 2018. It's only 40 minutes or so long.

Personally the biggest thing to understand about functional programming is it is "transformational" in its nature. y = f(x)   You put x in, you get y out. You put an array x in, you get a different array y out. And so on and so forth. But how does that translate into real world?

Consider this simple fact... a function is (sometimes) a representation of data, and vice versa.

Now you ask, what are you talking about? How do we represent "John Doe" as a function?

Consider this example... you know sin(x) and cos(x) trig functions, right? It does take a little time to compute. So you create lookup tables to make it go really fast.

But lookup table is just data. It's often implemented as a table or array of numbers.

So data is a function, and vice versa, at least in some circumstances.

Now remember this: function = data  / data = function.

So we can pass functions around just like data, and vice versa.

SIDE NOTE: JavaScript is not really designed to be functional when it was first conceived, but with ES6 it has gained a lot of features that allow it to be somewhat functional, provided you don't sneak in imperative features.

Now, let us compare some imperative programming in JavaScript, vs. the same function (same output), but in functional style:

WARNING: You need to know JavaScript ES6, such as fat arrow functions, to understand some of the code later. Please go review "fat arrow functions" first if you are not sure what it is.

Comparison Between Imperative vs Functional

Let's say, you need to tell if a certain person is legal to drink alcohol by checking their age. (Let's assume the legal age is 21, and yes, I am assuming the US.)  Imperatively, you'd probably write something like this:

//parameter: positive integer 'age'
let answer
if (age<21) {
  answer='Not legal to drink'
} else {
  answer='You can drink legally'
}
console.log(answer)

However, if you do it functionally, you'd write something like this:

//parameter is a positive integer 'age'
const legalToDrink = age => age >= 21
const getAnswer = age => legalToDrink(age) ? 'You can drink legally' : 'Not legal to drink' 
const answer = getAnswer(age)
console.log(answer)

Notice that the functional solution has (only) three lines, and each one is a function?

NOTE: The getAnswer line uses a ternary operator.  It can be a bit hard to understand at first, but please study it as it allows you often to shorten your code significantly, while not sacrificing readability.

WHY do it functionally? It is more modifiable and easier to debug. If you are doing this in a state where the legal drinking age is 20, not 21, you just need to change legalToDrink function. The rest of the program can stay the same.

Another thing to note: age was never modified. We use it in conditions, we process it to build a different value, but age itself was not changed.  This is a fundamental tenet of functional programming: do NOT change the original value. That's why you see a LOT of const declarations in functional programming.  It's sometimes referred to as "immutability".

This is a small example, as you can scan all lines of the imperative version in a second. But imagine something several magnitudes of complexity above this... hundreds of functions, thousands of lines. THEN you'd appreciate where the values cannot "mutate", and functions are easy to spot.

What's even nicer is the various functions can be exported and reused elsewhere.

In fact, that's how React structured its components... As rendering functions. But as this is not about React, I'll discuss React later.

How about a more complex example? Let us process an array looking for odd numbers

//parameter: array of integer 'arr' [1,2,3,4,5,6]
let odd = []
arr.foreach(el => {
  if (el % 2 !== 0) {
    odd.push(el)
  }

}
console.log(odd)

Now how would you write that as a function? You would use filter to pick out which ones to accept.

//array of integer 'arr', assume [1,2,3,4,5,6]
const isOdd = el => el % 2 !==0
const odd = arr.filter(el => isOdd(el))
console.log(odd)

What if we want to sum an array, or subtract an array, or something similar? For imperative form, you'd probably write:

//array of integer 'arr', assume [1,2,3,4,5,6]
let sum=0; 
for (let i=0;i<arr.length;i++) {
  sum+=arr[i]
}
console.log(sum)

Though if you know "foreach", you may write this instead, which is a bit more functional than imperative... 

//array of integer 'arr', assume [1,2,3,4,5,6]
let sum 
arr.forEach(el => {
  sum==null ? sum=el : sum+=el
  })
console.log(sum)

But there is yet another way... traversing left to right, we can use reduce. (or reduceRight if we need to traverse the other way, right to left)

//array of integer 'arr', assume [1,2,3,4,5,6]
function doSum (total, num) {
  return total+num }
sum=arr.reduce(doSum)
console.log(sum)

or we can write it as an arrow function

//array of integer 'arr', assume [1,2,3,4,5,6]
const doSum = (total,num) => total+num;
const sum=arr.reduce(doSum)
console.log(sum)

One last example, to demonstrate the use of map for functional programming. Let's say, we want to apply a sales tax of 8% to all orders. You'd probably use a for loop if you don't do functional... 

//array of float "arr", assume [10.5,27.2,18.7]
let withSalesTax = []
for (let i=0;i<arr.length;i++ {
    wishSalesTax.push(1.08*arr[i])
}
console.log(withSalesTax)

We can also use a forEach loop... 

//array of float "arr", assume [10.5,27.2,18.7]
let withSalesTax = []
arr.forEach(el => {
  withSalesTax.push(el*1.08)
  })
console.log(withSalesTax)

If you write that as a function, it'd be...

//array of float "arr", assume [10.5,27.2,18.7]
const withSalesTax = arr.map((el) => el*1.08)
console.log(withSalesTax)

Now, that wasn't so hard, was it?

So what did you see in common among the functional programming examples?

No (Normal) Loops

There are no more for (or foreach) and while loops in functional programming.

Instead, you have map, filter, reduce, or recursion (which we did not cover here) based on the parameter or array that needs to be transformed and/or traversed. There are variations, like reduceRight, or flatMap, but they just that: variations.

Do keep in mind that NOT ALL imperative programming can be directly "translated" into functional programming. But that is an advanced topic in itself. In fact, you should stop thinking of "loops", but instead, functional transformations of data.

On the other hand, this really feels as if we are just making extra work for ourselves... how does this really help us in the here and now when I just need some work done?

But How Does That Help Us? 

We did say we will not modify the original data (remember immutable?)

Well, sorta. What we do is we set up a SINGLE SOURCE OF TRUTH, and use functions to modify that truth. In modern React, this is called "state", and you need to use setState to change it, and it would be the ONLY place(s) where the states changes.

Think of state as a special "locked memory" where variables can be protected UNLESS you use special keys to get to them, sort of like safe deposit boxes.

By limiting the changes of state to the bare minimum, we can not only make debugging easier, but also base screen updates on them. React uses updates to state to help determine if the ReactDOM needs to be updated (i.e. redrawn).

SIDE NOTE: React can also use Redux to store the state instead of its built-in stuff to manage state. How and why is also beyond the scope of this article.

But Wait, There Are More!

There are functors, monads, currying, and more, that you need to learn. And that would be beyond the scope of this short article.

But don't worry, you can go through the videos by Mattias Petter Johansson aka mpj on Youtube

https://www.youtube.com/playlist?list=PL0zVEGEvSaeEd9hlmCXrk5yUyqUag-n84

This is not React, but they are fundamental to your understanding of React. Once you understand these, you would be in a MUCH better position to understand React.

We shall discuss how React.js fits into all this next.


Comments

Popular posts from this blog

Yet another take on recursion

The Bare Essential Guide to Git

How to Solve a Problem 5: Horse-Racing Duals and HyperDuals