Loïc TRUCHOT
JavaScript Fullstack Expert & Senior web developer
CC-BY-NC-4.0 / Mar. 2020 / Loïc TRUCHOT
How do you call people that can:
function openEyes () {
console.log("The truth is coming out");
}
openEyes();
const openMind = () => "It can make you blind.";
const mind = openMind;
const msg = mind();
What dot you see when you think about an exclusive OR ?
imperative
declarative
electricity -> transistors -> binary -> assembly -> AST -> language -> symbols
(true && true) && (true && false); // transistor
0b11 + 0b10; // binary
2 + 3; // imperative addition
add(2, 3); // declarative addition
Ritual
The only secret to controlling complexity is not to focus on the details.
function clone (o) {
return JSON.parse(JSON.stringify(o));
}
// OR shortly
const clone = (o) => JSON.parse(JSON.stringify(o));
Secret
a wise choice
// imperative, via statements
let statementMessage = '';
if (age < 18) {
statementMessage = 'You shall not pass!';
} else {
statementMessage = 'Welcome to the ceremony...';
}
console.log(statementMessage);
// declarative via expressions
function ifElse(cond, ok, notOk) {
if (cond) { // predicate
return ok; // consequent
}
return notOk; // alternative
}
console.log(ifElse(age < 18, 'Go back !', 'Welcome'));
// AKA ternary, which is an expression too
console.log(age < 18 ? 'Go back !' : 'Welcome');
What about a pure function ?
Ritual
and transparent
const prop = (key, obj) => obj[key];
Secret
including mutations
const not = (val) => !val;
const nlp = pipe(
purify,
parse,
splitWords,
inferMeaning,
combineResults
);
nlp("Some dangerous stuff.");
let content = 'Turn cat into bat';
const nouns = ['cat', 'owl', 'bat', 'wand'];
function extractNouns() {
const extractedNouns = [];
const arr = content.split(' ');
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < nouns.length; j++) {
if (nouns[j] === arr[i]) {
extractedNouns.push(arr[i]);
}
}
}
content = extractedNouns;
}
extractNouns();
console.log(content);
const includes = (arr, w) => {
for (let i = 0; i < arr.length; i++) {
if (arr[i] === w) return true;
}
};
const filterWords = (arr, words) => {
const filtered = [];
for (let i = 0; i < words.length; i++) {
if (includes(arr, words[i])) {
filtered.push(words[i]);
}
}
return filtered;
};
const extractNouns = (nouns, content) => {
const words = content.split(' ');
return filterWords(nouns, words);
};
const content = 'Turn cat into bat';
const nouns = ['cat', 'owl', 'bat', 'wand'];
const result = extractNouns(nouns, content);
console.log(result);
Readability / Reasonability ?
Name collision?
Value tracking ? Reason about ?
Reusability ? Testing ?
But still get functions dependencies...
+(3, 5) // 8
concat("Hello", " ", "World!) // "Hello World!"
combineWeddingVideos("drink.mp4", "dance.mp4")
Ritual
const avadaKedavra = (human) => `${human} is dead.`;
const episkey = (human) => `${human} is healed.`;
const spells = [avadaKedavra, episkey];
const rand = (arr) => arr[Math.floor(Math.random() * arr.length)];
const me = 'Loïc';
const result = rand(spells)(me);
console.log(result); // dead or alive?
Secret
Ritual
functions as input or as output
const laugh = () => console.log('Muhahaha...');
document.getElementById('poison').addEventListener('click', laugh);
Promise.resolve('spider web').then((obj) => console.log(`eat ${obj}`));
const forEach = (func, arr) => {
for (let i = 0; i < arr.length; i++) {
func(arr[i]);
}
};
forEach(console.log, ['Cthulhu', 'Shoggoth']);
['Cthulhu', 'Shoggoth'].forEach(laugh);
const map = (func, arr) => {
const newArr = [];
for (const el of arr) {
const newEl = func(el); newArr.push(newEl);
}
return newArr;
};
const eat = (thing) => `I just ate this ${thing}. Tasty.`;
map(eat, ['spider', 'crow']).forEach(console.log),
forEach(
(b) => console.log(`${b} blood liters`),
map((item) => item.blood, [
{ name: 'cat', blood: 2 },
{ name: 'snake', blood: 0.4 },
]),
);
{ 1, 2, 3 } -> double -> { 2, 4, 6 }
as examples of expressiveness and DRY
const setOnFire = (a) => {
const b = 'wood';
const burn = () => {
console.log(`${a}is burning in the ${b}`);
};
burn();
};
const setOnFire = (a) => {
const b = 'wood';
const burn = () => {
console.log(`${a}is burning in the ${b}`);
};
return burn;
};
const makeItBurn = setOnFire('bat');
makeItBurn();
const eat = (thing) => `I just ate a ${thing}. Tasty.`;
const forEach = (func) => (arr) => {
for (let i = 0; i < arr.length; i++) { func(arr[i]); }
};
const map = (func) => (arr) => { /* ... */ };
const logEach = forEach(console.log);
const eatAll = map(eat);
logEach(['spider', 'bat', 'cat', 'owl']);
logEach(eatAll(['Cthulhu', 'Shoggoth']));
Secret
Ritual
Refuse God Objects
[].map(); // OK
Animal.map(); // NOT OK
const beFrightening = a => a + " is frightening";
beFrightening("cat");
beFrightening("sky");
beFrightening("mystery");
combining functions
const prop = (key) => (obj) => obj[key];
forEach(
(b) => console.log(`${b} liters`),
)(
map(prop('blood'))([
{ name: 'cat', blood: 2 }, { name: 'snake', blood: 0.4 },
]),
);
const filter = (f) => (arr) => arr.filter(f);
const pipe3 = (f1, f2, f3) => (arg) => {
f3(f2(f1(arg)));
};
const add = (a) => (b) => a + b;
pipe3(add(111), add(111), console.log)(444);
pipe3(
filter((item) => item.blood > 0.1),
map(prop('blood')),
forEach((b) => console.log(`${b} liters`)),
)([{ name: 'cat', blood: 2 }, { name: 'snake', blood: 0.4 }]);
Secret
Ritual
const bools = [true, false, true];
const ints = [1, 2, 3];
console.log(bools[0] && bools[1] && bools[2]);
console.log(ints[0] + ints[1] + ints[2]);
const reduce = (operation, neutral) => (arr) => {
let acc = neutral;
for (const el of arr) {
acc = operation(acc, el);
}
return acc;
};
const sum = reduce((a, b) => a + b, 0);
const all = reduce((a, b) => a && b, true);
console.log(sum(ints));
console.log(all(bools));
const max = reduce((a, b) => (a > b ? a : b), -Infinity);
console.log(max(ints));
const pipe = reduce((f, g) => (arg) => g(f(arg)), (a) => a);
const add = (a) => (b) => a + b;
pipe([add(111), add(111), console.log])(444);
Secret
Ritual
recursion over loops
const list = ['spider', 'cat', 'bat'];
const recurse = (func, arr) => {
if (arr.length) {
const [head, ...tail] = arr;
func(head);
recurse(func, tail);
}
};
recurse(console.log, list);
I felt a great disturbance in the Force
Java
Ruby
C++
C#
Objective C
Angular
Scala
Elixir
Clojure
Rust
F#
Swift
React
Reason
Elm, PureScript...
Java 8 -> lambda
PHP 7 -> HOF...
LISP
Erlang
OCaml
Haskell
JS & Python ?
Function are alreadyfirst class citizen
Secret
...and prepare to be hated and burnt
By Loïc TRUCHOT
An introduction to Functional Programming with JavaScript, targeting new coders interested by this old paradigm.