Back
Close

Becoming Functional

LeoLanese
6,852 views

"OOP vs FP: Don't be an FP programmer, don't be an OOP programmer... BE A BETTER PROGRAMMER." ~ @fernando_cejas


To start with this workshops you need to have an undertanding of what Functional Programming is. You can check this previous workshop: https://tech.io/playgrounds/0ccbd2817eab67cc4e41211af5c23d1520042/becoming-functional/welcome-


To become Functional we need to start Thinking Functionally. Lets go throw some steps to get the right mindset into Functional Programming mindset to become into functional.


Use ES6 Arrow Functions (fat arrow) as much as posible

Why:

  • Arrow functions create a concise expression that "encapsulates" a small piece of functionality.
  • Additionally, arrows retain the scope of the caller inside the function eliminating the need of self = this. Remember: Minimize moving parts
// old style
var multiply = function(x,y) {
return x * y;
}
console.log(multiply(2,10)); // 20
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// Better do:
// ES6 style
const multiply = (x, y) => x * y;
console.log(multiply(2,10)); // 20
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Further Information: ES6 Arrow functions create a concise expression that encapsulates a small piece of functionality. Additionally, arrows retain the scope of the caller inside the function eliminating the need of self = this.


Use Function Delegation

Why: Function delegates encapsulate a method allowing functions to be composed or passed as data.

const addOne = n => n + 1;
const isZero = n => n === 0;
const addValues = (x,y) => x + y;
const giveMeTheKey = x => x.age === 38;
console.log(addOne(1)); // 2
console.log(isZero(addValues(-5, 5))); // True
console.log([0,1,0,3,4,0].filter(isZero).length); // 3
let yoGiveMeTheKey = [{
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

https://stackblitz.com/edit/function-delegation


Separate the pure from the impure

If a function is impure, if posible, split it and simple as creating two functions


Don't change objects in functions

// IMPURE MUTATED :(
const minimum = {
usa: { old: 16 },
spain: 21,
uk: 19
}
function save(object){
object.saved = true; // don’t
return object;
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// Better do:
// PURE
const minimum = {
usa: { old: 16 },
spain: 21,
uk: 19
}
// Write functions that return altered copies instead of changing properties of the given object.
function save(object){
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Better ifs or no ifs techniques

Expressions instead of Statements
// old style: 'Statement'
const sayHello = function(hour) {
var salutation; // temp value
if (hour < 12) {
salutation = "Good Morning";
}
else {
salutation = "Good Afternoon"
}
return salutation; // mutated value
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// Better do: 'Expression'
const sayHello = (hour) => hour < 12 ? "Good Morning" : "Good Afternoon";
console.log(sayHello(10)); // Good Morning
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

https://stackblitz.com/edit/use-expressions-instead-statements?file=index.js

Better ifs, no ifs: Avoid Nested ifs/complex || conditions
// old style
var myvar = 1;
if( myvar==1 || myvar==5 || myvar==7 || myvar==22 ) { true }; // true
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// better but not great :/
const myvar =1;
[1,5,7,22].indexOf(myvar)!== -1; // true
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// Better do:
// a modern approach
const myvar =1;
[1,5,7,22].includes(myvar); // true
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Better ifs, no ifs: Use functions

// old school
var color = 'red'
if (color) {
if (color === 'black') {
console.log('black');
} else if (color === 'red') {
console.log('red');
} else if (color === 'blue') {
console.log('blue');
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// Better do:
const red = function(){ return 'red' };
const blue = function(){ return 'blue' };
const yellow = function(){ return 'yellow' };
function getColor(type) {
let colors = {
'red': red,
'blue': blue,
'yellow': yellow,
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

https://stackblitz.com/edit/use-functions-no-ifs?file=index.js


Avoid loops and iterations

A loop is an imperative control structure that is hard to reuse and difficult to plug in to other operations. We can use: Recursion,.map(), .reduce(), .filter(), etc

Why: - make the code clean - make the logic reusable - minimizing moving parts
// Old style
// take an array of Object just get an specific key(s)
// Using old imperative programming
var ao = [{
"name": "Zak",
"age": 25
},{
"name": "Adel",
"age": 38
},{
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// better: using FP, avoid loops
const x = (x) => x.name; // minimizing moving parts
[{
"name": "Zak",
"age": 25
},{
"name": "Adel",
"age": 38
},{
"name": "Yori",
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// Using .some to break a loop
const isBiggerThan10 = numb => numb > 10;
[2, 5, 8, 1, 4].some(isBiggerThan10); // false
[12, 5, 8, 1, 4].some(isBiggerThan10); // true
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// Using .every to break a loop
const isSmallerThan10 = num => num < 10;
console.log([2, 5, 8, 1, 4].every(isSmallerThan10)); // true
console.log([12, 5, 8, 1, 4].every(isSmallerThan10)); // false
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Tip: Think about results over steps. Next time you are about to iterate something, stop, and think about: "How this can look if I don't iterate this?" Loops & Iteration


Use array manipulation functions & Avoid mutator methods

DO NOT use the mutator methods, these methods modify the array: .push(), .copyWith(), .fill(), .pop(), .reverse(), .shift(), .sort(), .splice(), .unshift()

Better USE non-mutating methods (Accessor methods, Iteration methods ): .concat(), .join(), .slice(), .toString(), .reduce(), .reduceRight(), etc.

Full list:

Mutator_method


Use Higher-order-Function (HoF) & Spread Operator when possible

let jsonData = [
{ id: 1, name: "Soda", price: 2.40, cost: 1.04, size: "4cl", },
{ id: 2, name: "Beer", price: 6.00, cost: 2.45, size: "8cl" },
{ id: 3, name: "Margarita", price: 10, cost: 4.45, size: "12cl" }
];
// ...x (spread operator) ensures that we copy the complete object and its properties, while only modifying the price value.
const objAndProperties = jsonData
.map(x => ({...x, price: (x.price / 2).toFixed(1) }));
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
//
var arrOfObj = [
{ "name": "Sam", "age": 1 },
{ "name": "Tom", "age": 2},
{ "name": "Carley","age": 35}
];
// this is so 2014 - ES4.1
var arrToFilter = [];
for (var i=0; i < arrOfObj.length; i++){
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Use Method Chaining

Method chains allow a series of functions to operate in succession to reach a final result. Method chains allow function composition similar to a pipeline.

let cart = [{name: "Drink", price: 3.12},
{name: "Steak", price: 45.15},
{ name: "Drink", price: 11.01}];
let drinkTotal = cart.filter(x=> x.name === "Drink")
.map(x=> x.price)
.reduce((t,v) => t +=v)
.toFixed(2);
console.log('Total Drink Cost', drinkTotal ); // Total Drink Cost $14.13
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Use pipelines

A pipeline allows for easy function composition when performing multiple operations on a variable. Since JavaScript lacks a Pipeline operator, a design pattern can be used to accomplish the task.

const pipe = functions => data => {
return functions.reduce(
(value, func) => func(value),
data
);
};
let cart = [3.12, 45.15, 11.01];
const addSalesTax = (total, taxRate) => (total * taxRate) + total;
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Dependency injection

Dependency injection works by moving the impure parts of the code out of the function. So you have to pass them in as parameters.

// Here we are using:
// new Date()).toISOString() = IMPURE
// console.log(); = IMPURE
const d = {toISOString: () => '1865-11-26T16:00:00.000Z'};
// IMPURE
function logSomething(foo) {
const dt = (new Date()).toISOString(); // Date is impure
console.log(`${dt}: ${foo}`); // IO log is impure
return foo;
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// make it pure using dependency injection:
// take any impurities and make them a function parameter now we'll have 3 params:
function logSomething(d, cnsl, foo) {
const dt = d.toISOString();
cnsl.log(`${dt}: ${foo}`);
return foo;
}
// then we call it with the impure parts
const foo = "make it pure using dependency injection!"
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

sum all values of the Array

// forEach not nice :(
// forEach() is still a loop
const values = [1, 2, 3, 3, 5];
let sum = 0;
values.forEach( v => { sum += v; } ); // forEach() is still a loop
console.log( sum ); // 14
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// Better do:
// getting rid of loops
const values = [1, 2, 3, 3, 5];
const sum = values.reduce( (acum, v) => {
const result = acum + v;
return result;
}, 0 );
console.log( sum ); // 14
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Further information: Array.prototype.reduce()


Get cats younger than 7 months

const cats = [
{ name: 'Mojo', months: 84 },
{ name: 'Mao-Mao', months: 34 },
{ name: 'Waffles', months: 4 },
{ name: 'Pickles', months: 6 }
]
var kittens = []
// old `for loop` :(
for (let i = 0; i < cats.length; i++) {
if (cats[i].months < 7) {
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// better do :)
const cats = [
{ name: 'Mojo', months: 84 },
{ name: 'Mao-Mao', months: 34 },
{ name: 'Waffles', months: 4 },
{ name: 'Pickles', months: 6 }
]
const isKitten = cat => cat.months < 7; // check reusability moving parts!
const getName = cat => cat.name; // sexy code :)
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Further Information: Array.prototype.filter()


Lazy functions

Controlling the side-effect: A side effect isn’t a side effect until it actually happens.

// fZero() is impure
function fZero() {
console.log('IMPURE'); // IO log = Impure
return 0;
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// NOW returnZeroFunc() is pure, the function does nothing other than return the same fZero function, every time.
function returnZeroFunc() {
function fZero() { // we wrapped fZero() inside another function that just returned it right?
console.log('IMPURE');
return 0;
}
return fZero;
}
// This function wrapping thing is a legitimate strategy.
// We can keep hiding behind functions as long as we want. Oh yes!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Variable Assignment

function doubleAndAddTen(x) {
    const doubled = x * 2;
    return doubled + 10;
}
// The doubled binding is now a function parameter, which doesn't modify the local scope for doubleAndAddTen.
function doubleAndAddTen(x) {
return (doubled => doubled + 10)(x * 2);
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Sequenced Side Effects

// Impure
console.log('One');
console.log('Two');
function pureLog(msg) {
    return () => console.log(msg);
}

const sideEffectSequence = (firstEffect, secondEffect) =>
    () => (ignoredReturnValue => secondEffect())(firstEffect());

const sequencedSideEffect = sideEffectSequence(pureLog('One'), pureLog('Two'));

sequencedSideEffect(); // One, Two

Avoid impure methods

Date (Date.now), Math.random (since it always produces a new value no matter what the inputs are), console.log(), this, global variables, exceptions thrown, etc. In fact, because JavaScript passes object references around, every function that takes an object or array is potentially subject to impurity.


Bend your language to the problem, not the problem to the language

The most obvios tip is: Please, take advantage of all the language features, if you have something that it is already done, well: use it. Do not spend time working on languague problem, focus on bussiness goal issues.


Create your playground on Tech.io
This playground was created on Tech.io, our hands-on, knowledge-sharing platform for developers.
Go to tech.io
codingame x discord
Join the CodinGame community on Discord to chat about puzzle contributions, challenges, streams, blog articles - all that good stuff!
JOIN US ON DISCORD
Online Participants