DEV Community

Cover image for 3 clean code principles for the functional programming beginner
Ayabonga Qwabi
Ayabonga Qwabi

Posted on

3 clean code principles for the functional programming beginner

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
Enter fullscreen mode Exit fullscreen mode

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 = []
Enter fullscreen mode Exit fullscreen mode

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"
    }
]
Enter fullscreen mode Exit fullscreen mode

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 = () => {}
Enter fullscreen mode Exit fullscreen mode

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)

Enter fullscreen mode Exit fullscreen mode

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`)
Enter fullscreen mode Exit fullscreen mode

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`)
Enter fullscreen mode Exit fullscreen mode

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}`)
)
Enter fullscreen mode Exit fullscreen mode

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 )
}
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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

Top comments (10)

Collapse
 
mattmcmahon profile image
Matt McMahon

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. 👏👏👏👏

Collapse
 
1crazymoney profile image
1Crazymoney

Yes you are correct sir

Collapse
 
sirseanofloxley profile image
Sean Allin Newell

+1 for the code potatoe.

Collapse
 
davidvalenciait profile image
davidvalencia-it

Thank u !

Collapse
 
haaxor1689 profile image
Maroš Beťko

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

Collapse
 
douglasvincent profile image
Douglasvincent

Thanks for the potatoe

Collapse
 
ken11zer01 profile image
ken11zer01

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

Some comments may only be visible to logged-in visitors. Sign in to view all comments.