Functional Programming basics with example

Howdy! This is mostly stand-alone content, for greater context you can read more here.

Coding is fun! And coding can be very hard, the worst part is how beginner programmers can shoot themselves in the foot and make things harder than they need to be. One paradigm of coding that helps reduce errors in data transformation is functional programming. If done right it also makes code very testable, so if something breaks you can see where it broke easily.

If you want to learn what makes some code more difficult than others, read on!

let data1 = 'This is a fairly short string.'
let data2 = [ 1,2,3,4,2,4,2,3,4,5,2,5,1,3 ]
function dataToFrequency(data){
switch(typeof data){
case 'string':
return arrayToFrequency(stringToArray(data))
break;
case 'object':
return arrayToFrequency(data)
break;
default:
console.log('Error: New data type in dataToArray()')
}
}
function stringToArray(string){
return string.split(' ')
}
function arrayToFrequency(array){
let frequency = {}
array.forEach(character => {
frequency[character]
? frequency[character] += 1
: frequency[character] = 1
})
return frequency
}

This one has three functions to fulfill the job done by one function earlier. At the top we have a well trained front line worker, with three instructions. In situation ‘A’ initiate plan A then stop. In situation ‘B’ initiate plan B then stop. If it’s not situation ‘A’ or ‘B’ report back to me! Clearly there are more than two types of data and we haven’t even crossed the typeof [] & typeof {} = ‘object’ bridge yet. That’s not the point of this example.

The point of this example is that if you’re coding to change state to clean data for a future process. You’ve got to code twice. Once to decide which conveyer belt to send your data down to make it clean, and a second time with how each conveyer belt modifies your data.

Now the most horrific thing I see in this mess is the dataToFrequency function is doing more than one job. Is dataToFrequency the decision switch? Is it converting the array to the object? If this breaks how do I test for where in the chain it broke? dataToFrequency is wearing a lot of hats, if something breaks it may not be clear where the breakage occurred. In functional programming we want to bring that down as much as we can.

function dataToFrequency(data){
let cleanData = dataToArray(data)
return arrayToFrequency(cleanData)
}
function dataToArray(data){
switch(typeof data){
case 'string':
data = stringToArray(data)
break;
case 'object':
break;
default:
console.log('Error: New data type in dataToArray()')
}
return data
}
function stringToArray(string){
return string.split(' ')
}
function arrayToFrequency(array){
let frequency = {}
array.forEach(character => {
frequency[character]
? frequency[character] += 1
: frequency[character] = 1
})
return frequency
}

Above we took dataToFrequency and renamed it dataToArray, and made a new function dataToFrequency that wraps the data transformation process. This renaming better matches what each function is actually doing, and breaks up the tasks being performed.

Now the new dataToArray function still wears a few hats and can be refined further. But two things have changed. One: if data enters the dataToArray function there will always be a defined return value. Two: if it returns something that isn’t an array we know arrayToFrequency is uninvolved.

This makes testing for and finding the broken piece much easier.