loading...
Cover image for 3 clean code principles for the functional programming beginner

3 clean code principles for the functional programming beginner

ayabongaqwabi profile image Ayabonga Qwabi ・4 min read

1. Name things meaningfully

When you name a variable the name you give it must tell us that variable's whole life story. It must tell us who the variable is and why it is. The name is a selling point of a variable and that's why it must be properly described.

It should tell you why it exists, what it does, and how it is used. If a name requires a comment, then the name is not meaningful.

Alt Text

consider the variable

const p = [] //list of pets

P can be anything, which would make it harder to read when this variable is used within a complex loop or function.

This is a more meaningful name

const bertsPetList = []

because it tells you

What? list of pets belonging to bert
Why? for code operations that are interested in Bert and his pets
How? as a standard js array

1.2 Function names must reveal intent

When naming a function we must also think of "What?", "Why?" and "How?"

What does it do?
Why does it do this?
How does it do this?

Take for example you wanted to get a list of animals specific owners might have that are allowed to stay with their owner inside the house.

const bertsPets = [
    {
       name: "Snizzles"
       type: "nope"
       lives: "outdoors"
    },
    {
       name: "Terrance"
       type: "danger-woof"
       lives: "outdoors"
    },
    {
       name: "Kevin"
       type: "doggo"
       lives: "indoors"
    }
]

For instance the name for such a function could be findPets, as much as the name makes sense it wouldn't be descriptive enough for the next programmer who's going to read your code to easily understand what's going on.

So maybe you would try the name findPetsThatLiveIndoors

Which is good but in terms of DRY (we'll get into this in the next section) you are doing your code a disservice, because for every living area type you will have to create a function corresponding to that type
i.e

const findPetsThatLiveIndoors = () => {}
const findPetsThatLiveOutdoors = () => {}
const findPetsThatLiveInOtherPlace1= () => {}
const findPetsThatLiveInOtherPlace2 = () => {}

Thereby unnecessarily repeating yourself. (Which is bad)
So what name can we give our function?

const filterPetsByLivingAreaInList  = () => {}

// which could then be

const filterPetsByLivingAreaInList  = (area, list) => list.filter(pet => pet.lives === area)

// and can produce

const bertsIndoorPets = filterPetsByLivingAreaInList('indoors',bertsPets)

Now this name tells us the
what? pets that live in a specific area
how? by filtering a list
why? to get a list of animals which a specific owner might have that he/she allows to live inside the house

2. Do not Repeat Yourself

The DRY principle simply means you should not have code duplications.

Alt Text

2.1 Variable scopes

Don't recreate variables for each and every function scope when a global scope can be used
e.g

const getDoggosThatLiveIndoors = () => {
    const doggos = getPetsByType('doggo', bertsPets);
    const doggosThatLiveIndoors = filterPetsByLivingAreaInList('indoors', doggos);
    return doggosThatLiveIndoors;
}

const getDoggosThatLiveOutdoors= () => {
    const doggos = getPetsByType('doggo', bertsPets);
    const doggosThatLiveIndoors = filterPetsByLivingAreaInList('indoors', doggos);
    return doggosThatLiveOutdoors;
}

console.log(`${getDoggosThatLiveIndoors().length} doggos live indoors`)
console.log(`${getDoggosThatLiveOutdoors().length} doggos live outdoors`)

In the above example the variable doggos can be defined in the global scope to avoid re-defining it for every function

const doggos = getPetsByType('doggo', bertsPets);

const getDoggosThatLiveIndoors = () => {
    const doggosThatLiveIndoors = filterPetsByLivingAreaInList('indoors', doggos);
    return doggosThatLiveIndoors;
}

const getDoggosThatLiveOutdoors = () => {
    const doggosThatLiveIndoors = filterPetsByLivingAreaInList('outdoors', doggos);
    return doggosThatLiveOutdoors;
}

console.log(`${getDoggosThatLiveIndoors().length} doggos live indoors`)
console.log(`${getDoggosThatLiveOutdoors().length} doggos live outdoors`)

2.2 Function operations

In the above example the two functions getDoggosThatLiveIndoors and getDoggosThatLiveOutdoors perform the same operation and can therefore be optimized into one

const doggos = getPetsByType('doggo', bertsPets);

const getDoggosByLivingArea = (areaType) => {
    const doggosInArea = filterPetsByLivingAreaInList(areaType, doggos);
    return doggosInArea;
}

const areaTypes = ['indoors', 'outdoors'];

areaTypes.map( type => 
    console.log(`${getDoggosByLivingArea(type).length} doggos live ${type}`)
)

Duplication may be the root of all evil in software. Many principles and practices have been created for the purpose of controlling or eliminating it.

3. Functions should do one thing

When creating our functions we should make sure that they achieve only one defined goal

Now imagine the following function

const favoritePets = ['cat', 'doggo']

const getFavoritePets = (favoritePets, petList) => {
       const ownerHasCats = hasPetType('cats', petList);
       if(!ownerHasCats){
          const cats = [cat1, cat2, cat3]
          const petsWithCats = insertPets(cats, petList)
          return filterPets(favoritePets, petsWithCats )
       }
       return filterPets(favoritePets, petList )
}

This function should only be getting the owner's favorite pets but it also tries to find out if the owner's cats have been added to his pet list and inserts them if they aren't available. This violates the Single Responsibility Principle because this function is doing too many things. It has many responsibilities. It's name is getFavoritePets
not getFavoritePetsAndCheckIfOwnerHasCatsIfNotAddCatsToTheOwnersPetList
😂

A better way of doing this would be

const cats = [cat1, cat2, cat3]

const bertsPetsWithCats = insertPets(cats, bertsPets)

const favoritePets = ['cat', 'doggo']

const getFavoritePets = (favoritePetTypes, petList) => filterPets(favoritePetTypes, petList);

const bertsFavoritePets = getFavoritePets(favoritePets, bertsPetsWithCats);

Recap

There are 3 basic principles we must follow in order to write clean code in a functional programming paradigm.

  1. Name things meaningfully
  2. Do not Repeat Yourself
  3. Functions should do one thing

For more in depth knowledge on clean code I suggest your read the clean code handbook

And We're done :)

Here's a code potato

Alt Text

Posted on by:

ayabongaqwabi profile

Ayabonga Qwabi

@ayabongaqwabi

Grew up tweaking batch files. Now an avid lover of UX. Might be an imposter. Puts sugar in his coffee.

Discussion

markdown guide
 

Only thing better than a well named variable is a variable that doesn't need a name at all. Many of these functions could be written in a point-free style and composed or piped together. Add some higher order functions to simplify, e.g. filterPetsBy, and you'd have an excellent follow up to a great beginner article. 👏👏👏👏

 
 
 
 

the getCatsAnd... method kind we called an ensure... and everyone new that these methods did stuff that was too bullshit to call properly.

 
 

You got the potato from the "Potato Pirate" card game right?

 

Using global variables is a smell and common source of bugs, especially in a language like javascript. I'd recommend to read the book Clean Code. It has code examples in Java but the principles are language agnostic.

 

I see what he's getting at.

I don't like how he explained it.

The idea that the doggos variable shouldn't be duplicated is pretty self-evident, as it's both constant and effectively an additional helper function.

I doubt it belongs in the global scope, but it's reasonable to put it into the same scope as the functions that use it.

 

You are right it is a constant, I just wanted to highlight the duplication part from a beginners perspective