Open Source Your Knowledge, Become a Contributor
Technology knowledge has to be shared and made accessible for free. Join the movement.
The Pure (or Impure) game.
- You need to answer if the next functions are Pure Or Impure
- The question are in increasing level of difficulty.
** Level 1 **
** Level 2 **
** Level 3 **
** Level 4 **
LEVEL 1
but why?
Impure!
Impure function A side-effect here is a change of system state: it is no completely independent of outside state, due to this external call to non-constants we break the rules:
- Given the same inputs, always returns the same output
- No side-effects
FP avoids shared state, instead relying on immutable data structures and pure calculations.
const minimum = 21; // the calling to minimum makes it impure
but why?
Pure!
const minimum = 21; // calling minimum inside block scope make it pure
We can also makes the minimum immutable:
const immutableState = Object.freeze({ minimum: 21 });
but why?
Impure! Function depending on a value outside of its arguments.
// make it pure
function getDocument(environment) {
return environment.window.document;
}
getDocument(global);
LEVEL 2
but why?
Impure!
Object.freeze() frozen objects but only superficially, do not freeze internally
// minimum object is mutable
minimum.usa.old = 60; // "{"usa":{"old":60},"spain":21,"uk":19}"
// if we want to make it freeze we need to freeze every object
Object.freeze(minimum.usa);
// then
'use strict';
minimum.usa.old = 20; // Cannot assign to read only property 'old' of object '#<Object>'
Further information: Object.freeze()
but why?
Pure!
- .map() is a pure method which does NOT mutate data.
- There are no side effects using .map()
but why?
Pure! slice method don't mutate
console.log( arr.slice(0,3) ); // [1,2,3]
console.log( arr.slice(0,3) ); // [1,2,3]
console.log( arr.slice(0,3) ); // [1,2,3]
but why?
Impure! slice method mutate
arr.splice(0,3); // [1,2,3]
arr.splice(0,3); // [4,5]
arr.splice(0,3); // []
Tip: .splice() and .slice() They are two functions that do the exactly same thing, but mutation is where the difference is*
but why?
const arr = [1,2,3,4,5]; arr.reverse(); // [5, 4, 3, 2, 1] reverse mutate here arr.sort(); // [1, 2, 3, 4, 5] and sort here
[...arr].reverse(); // [5, 4, 3, 2, 1] but spread operator we can avoid mutation creating a new object [...arr].sort(); // [1, 2, 3, 4, 5] but spread operator we can avoid mutation creating a new object
.reverse() and .sort() both mutate, but using the spread operator we can avoid mutation
LEVEL 3
but why?
Origin object unchanged: {type: 'truck'}
const addWheels = (obj, num) => Object.assign({}, obj, {wheels: num}); // pure ES5 way
const noWheelTruck = {type: 'truck'};
const fourWheelTruck = addWheels(noWheelTruck, 4); // {type: 'truck', wheels: 4}
console.log(noWheelTruck); // original object unchanged: {type: 'truck'}
const addWheels = (obj, num) => {obj['wheels'] = num};
const noWheelCar = {type: 'car'};
const fourWheelCar = addWheels(noWheelCar, 4);
console.log(noWheelCar);
but why?
Impure! Origin object changed: { type: 'car', wheels: 4 }
const addWheelsImpure = (obj, num) => {obj['wheels'] = num};
const noWheelCar = {type: 'car'};
const fourWheelCar = addWheels(noWheelCar, 4); // impure {type: 'car', wheels: 4}
console.log(noWheelCar) // MUTATED! origin object changed: { type: 'car', wheels: 4 }
LEVEL 4
but why?
Pure!
"A side effect isn't a side effect until it actually happens": Our returnZeroFunc() function does nothing other than return the same function, every time.
We wrapped fZero() inside another function that just returned and keep it in test:
- This function wrapping thing is a legitimate strategy.
- We can keep hiding behind functions as long as we want.
- As long as we never actually call any of these functions, they’re all theoretically pure.
- Wrapping everything in a function lets us control those effects with precision. We decide exactly when those side effects happen.