Functionλl
Style
JλvλScript
FunctionΛl Style
The borrowing of functional programming concepts into multiparadigm programming languages.
JavaScript is NOT a functional Language
Nor is it a classical object Oriented language
JavaScript is a Prototypal Multiparadigm Dynamic Scripting Language
However, with some care and attention, we can extract quite a pleasant set of functional style patterns from JavaScript
Getting in the Functionλl Mindset
Zen
Free
Your
DΛtΛ
From
BehΛvior
Unlike OOP -- which intentionally couples data (attributes) and behavior (methods) together in an "object" -- FP concerns itself primarily with behavior (functions), and constructs flows that moves data through these behaviors.
class Crew {
constructor(awayTeam) {
this.awayTeam = awayTeam;
...
}
sendAwayTeam() {
let i = 0, len = this.awayTeam.length;
for (i; i < len; i++) {
let member = this.awayTeam[i];
if (member.shirt === 'red') {
this.awayTeam.splice(i, 1);
}
}
}
}
const wontDie = ({shirt}) =>
shirt !== 'red';
const sendAwayTeam = (team) =>
team.filter(wontDie);
const crew = new Crew([...]);
crew.awayTeam.length; // 5
crew.sendAwayTeam();
crew.awayTeam.length; // 4
const crew = { awayTeam: [...], ... };
crew.awayTeam.length; // 5
sendAwayTeam(crew.awayTeam);
crew.awayTeam.length; // 5
const crew = { awayTeam: [...], ... };
crew.awayTeam.length; // 5
const updatedCrew = {
...crew,
awayTeam: sendAwayTeam(crew.awayTeam)
}
updatedCrew.awayTeam.length; // 4
vs.
http://codepen.io/uniqname/pen/dpXvwV
http://codepen.io/uniqname/pen/xEOqZq
Develop
Λn
Λversion
to
stΛte
State is like a parasitic vine binding your code to its current use case, immobilizing it.
It slowly spreads its twisting tentacles to every part of the application causing small changes in one part to have cascading effects in several otherwise unrelated parts.
To understand one piece, one has to understand the entirety of the application.
Left unchecked or uncontrolled, state infects your application making it less resilient, less reliable, and less maintainable.
Fun & Useful fact: Impure functions increase cognitive load on developers and de-optimize compilers.
In contrast, black boxed, or pure functions are completely portable, unbound to your application.
One need only understand the function in isolation to contribute meaningfully to its maintenance or performance.
It's portability make is far easier to test.
Pure functions inoculate those parts of your application from the infection of unregulated state.
Fun & Useful fact : Pure functions break less than impure functions.
Learn
The
What &
Why of
Currying
Currying
Λs in hΛskell Curry
Turning a single function that takes multiple arguments into multiple functions that each take a single argument.
const add = (a, b) => a + b;
const add = (a) => (b) => a + b;
add('Iron', 'Carbon'); // Steel
add('Iron')('Carbon'); // Steel
const IronAnd = add('Iron'); // function
const IronAnd = add('Iron'); // function
IronAnd('Wine'); // Awesome music
NeΛt trick... now why would I ever use thΛt?
Real world example.
const tap = (message) => (data) => {
console.log(message, data);
return data;
}
somePromise
.then(tap('initial resolve: ')) // initial resolve: [someValue]
.then(someSortofTransform)
.then(tap('result of transform: ')) // result of transform [someOtherValue]
Think
In SmΛll,
Generic
Units Of
Work
generic is the key word here. Look for and abstract patterns.
Curried generic functions CΛn get more specific & speciΛlized down the Λrgument chΛin
const hasPropOfVal = (prop) => (targetValue) => (obj) => obj[prop] === targetValue;
famousShips.filter(isSerenity);
const hasNameOf = hasPropOfVal('name');
const isSerenity = hasNameOf('Serenity');
compose
Functions
With
Other
Functions
`compose` or `pipe`, both effectively accomplish the same thing. Use which ever you prefer.
Composition
Combining two or more functions to create a single new composite function.
const pluck = (propName) => (obj={}) => obj[propName];
const mapTo = (propName) => (list=[]) => list.map(pluck(propName))
const dedupePrimitives = (list) => [...(new Set(list))];
const reInflateByPropFromSrc = (prop) => (src) => (list) =>
list.map(item => src.find(i => i[prop] === item));
const dedupeBy = (someProp) => {
};
const reInflateFromSrc = reInflateByPropFromSrc(someProp);
return (list) => compose(
)(list);
reInflateFromSrc(list)
dedupePrimitives
mapTo(someProp)
http://codepen.io/uniqname/pen/XjKJxK
Composition
Many libraries come with a compose helper (lodash, underscore, Redux, Ramda), but it's also easy to roll your own.
const compose = (...fns) => (val) => fns.reduceRight((v, fn) => fn(v), val);
or pipe
const pipe = (...fns) => (val) => fns.reduce((v, fn) => fn(v), val);
Higher Order Functions
https://www.flickr.com/photos/75491103@N00/18958590963/in/photolist-3c4nvH-dfXHgt-9XLrcz-uTiJzZ-vxy45q-vxytBb
Higher Order Function
Any function which either accepts a function as an argument, or returns a function.
const map = function (mappingFn, list) {
return list.map(mappingFn);
}
const getValueOf = function (propName) {
return function (obj) {
return obj[propName];
}
}
H.O.F. Tip #1
Λrrow function (LΛmbdΛ) Λll the Things
I know much smarter people than me have said arrow functions aren't for every scenario, and they are right, but seek to minimize scenarios where they can't be used.
Higher Order Function
Arrow functions are particularly beneficial for returning functions, demonstrating visually the successive flow.
const map = (mappingFn, list) =>
list.map(mappingFn);
const getValueOf = (propName) => (obj) =>
obj[propName];
This makes currying fun and easy!
H.O.F. Tip #2
Write only single Arity Functions (Currying)
At least until you get very comfortable with currying and why it is valuable.
function makeRequest(appKey, user, url) {
if (validateAppKey(appKey)) {
if (authorizedUser(user)) {
return request(url);
} else {
return {
statusCode: 401,
message: 'user not authorized'
};
}
} else {
return {
statusCode: 401,
message: 'app not authorized.'
};
}
}
const makeRequest = (appKey) => (user) => (url) => {
if (validateAppKey(appKey)) {
if (authorizedUser(user)) {
return request(url);
} else {
return {
statusCode: 401,
message: 'user not authorized'
};
}
} else {
return {
statusCode: 401,
message: 'app not authorized.'
};
}
}
const makeAppRequest = makeRequest('empire.gov');
const makeAppRequestAsUser = makeAppRequest('L0rD_V8r');
makeAppRequestAsUser('/api/v1/death-star/plans');
Currying your functions
The Tλo of Blackboxing
A device, system or object which can be viewed in terms of its inputs and outputs without any knowledge of its internal workings. Its implementation is "opaque" (black)
BlΛck Box
wikipedia.org "Black box" (https://en.wikipedia.org/wiki/Black_box)
Blackbox
input
output
BlΛck Box
{ ... }
(...in) =>
return ...
A blΛck box is "pure" in Λ functionΛl sense.
Functional purity is a principle of functional programing and is defined as...
...given the same input, will always return the same output and does not have any observable side effect.
https://drboolean.gitbooks.io/mostly-adequate-guide/content/ch3.html
A side effect is defined as...
...a change of system state or observable interaction with the outside world that occurs during the calculation of a result.
https://drboolean.gitbooks.io/mostly-adequate-guide/content/ch3.html
Signs of Impurity
implicit dependencies.
export default (secret) =>
base64(secret);
export default (secret) =>
base64(secret);
import base64 from 'base64';
export default (secret='') =>
base64(secret);
BlΛck Boxing Tip #1
MΛke Λll dependencies Explicit
Either import dependencies into a single responsibility module, or pass them into your single responsibility function.
SIGNS of Impurity
ExternΛl Λssignments
const arr = [...]
let flag = false;
for (let i = 0, len = arr.length; i < len; i++) {
if ( arr[i].hasValue ) {
flag = true;
}
}
const arr = [...]
let flag = false;
for (let i = 0, len = arr.length; i < len; i++) {
if ( arr[i].hasValue ) {
flag = true;
}
}
const arr = [...];
const flag = arr.some((item={}) => item.hasValue);
BlΛck Boxing Tip #2
initiΛlizing or reΛssigning vΛriΛbles is Λ Smell.
This is a sign of an external dependency. If it's needed, pass it around like a bad white elephant gift.
SIGNS of Impurity
MutΛted Λgrugments
const killjoys = {
"Dutch": "The Leader",
"John Jaqobis": "The Trusted Friend"
};
const addToTeam = (name, role) => (team) => {
team[name] = role;
return team;
};
addToTeam("D'avin Jaqobis", "The Bad Boy")(killjoys);
const killjoys = {
"Dutch": "The Leader",
"John Jaqobis": "The Trusted Friend"
};
const addToTeam = (name, role) => (team) => {
team[name] = role;
return team;
};
addToTeam("D'avin Jaqobis", "The Bad Boy")(killjoys);
const killjoys = {
"Dutch": "The Leader",
"John Jaqobis": "The Trusted Friend"
};
const addToTeam = (name='', role='') => (team={}) => ({
...team,
[name]: role;
});
const complete = addToTeam("D'avin Jaqobis", "The Bad Boy")(killjoys);
BlΛck Boxing Tip #3
See ImmutΛbles Tip #3
Patience, we're getting there.
SIGNS of Impurity
ReferentiΛl OpΛcity
const randomBetween = (min=0, max=10) =>
Math.floor(Math.random() * max) + min;
SIGNS of Impurity
ReferentiΛl OpΛcity
Ok, so you can't always enforce functional purity, but you can control when and where it happens.
BlΛck Boxing Tip #4
Push Impurities to the edges of your ΛpplicΛtion
When data first arrives, or is about to leave the application flow (e.g. network response/requests, file system access, rendering, etc.)
SIGNS of Impurity
Neglecting the Return value
A dead giveaway that a function is impure is if it makes sense to call it without using its return value. For pure functions, that's a noop.
— Eric Elliott (@_ericelliott) August 29, 2016
The TΛo of the blΛckbox is the pΛth of purity
The Immutλble Power of Immutλbles
const iconic = 'To boldly go where no one has gone before';
const englishSnob = iconic.replace('boldly go', 'go boldly');
// 'To go boldly where no one has gone before'
iconic; // 'To boldly go where no one has gone before'
const num = 42;
num.toPrecision(3); // '42.0'
num; // 42;
Primitives Λre immutΛble
-
numbers,
-
strings,
-
symbols,
-
booleans,
-
null,
-
undefined
const obj = {
theTruth: 'bend the spoon'
};
obj.theTruth = 'there is no spoon';
obj; // { theTruth: 'there is no spoon' }
const arr = [1, 1, 3, 8];
arr.unshift('THX')
arr; // ['THX', 1, 1, 3, 8];
Objects Λre mutΛble
Objects, Arrays, Functions
const fn = () => {};
fn.$inject = ['does', 'this', 'look', 'familiar', '?'];
fn.$inject; // ['does', 'this', 'look', 'familiar', '?']
Why FΛvor ImmutΛbility?
StΛte!
(or lack thereof)
A reliance on external state is one of the most common sources of unintentional side effects.
-cory brown
ImmutΛble dΛtΛstructures require the creΛtion of Λ new context for every new vΛlue
This prevents one from inΛdvertently Λffecting shΛred stΛte...
...SINCE THERE EFFECTIVELY IS NONE.
Some JΛvΛScript Methods mutΛte their contexts.
Others return Λ new context.
IMMUTΛBLES Tip #1
Forget the mutΛting methods even exist. 🙈
Except when peer reviewing 🤔
const HPActors = ['Richard Harris', 'Daniel Radcliff', 'Emma Watson', 'Rupert Grint'];
HPActors.splice(0, 1); //['Daniel Radcliff', 'Emma Watson', 'Rupert Grint']
HPActors.push('Michael Gambon');
//['Daniel Radcliff', 'Emma Watson', 'Rupert Grint', 'Michael Gambon']
const HPActors = ['Richard Harris', 'Daniel Radcliff', 'Emma Watson', 'Rupert Grint'];
PoAActors = HPActors
.slice(1) //['Daniel Radcliff', 'Emma Watson', 'Rupert Grint']
.concat('Michael Gambon')
//['Daniel Radcliff', 'Emma Watson', 'Rupert Grint', 'Michael Gambon']
Not So Good (splice, push)
Not So Bad (slice, concate)
So THΛts cool Mr. Presenter, but objects don't hΛve methods like thΛt. Objects GottΛ Λssign.
- probably someone in the audience.
const crewStatus = {
Zoe: 4, Wash: 4,
Kaylee: 4, Jayne: 2
};
crewStatus.Jayne = 0; // Jayne got stupid in Ariel
Not So Good
const crewStatus = {
Zoe: 4, Wash: 4,
Kaylee: 4, Jayne: 2
};
delete crewStatus.Jayne; //You betray one of my crew, you betray me.
Even worse
IMMUTΛBLES Tip #2
Freeze RΛy
const HPActors = ['Richard Harris', 'Daniel Radcliff', 'Emma Watson', 'Rupert Grint'];
PoAActors = HPActors
.slice(1) //['Daniel Radcliff', 'Emma Watson', 'Rupert Grint']
.concat('Michael Gambon')
//['Daniel Radcliff', 'Emma Watson', 'Rupert Grint', 'Michael Gambon']
Not So Bad
const HPActors = Object.freeze(['Richard Harris', 'Daniel Radcliff', 'Emma Watson', 'Rupert Grint']);
PoAActors = Object.freeze(HPActors
.slice(1) //['Daniel Radcliff', 'Emma Watson', 'Rupert Grint']
.concat('Michael Gambon'))
//['Daniel Radcliff', 'Emma Watson', 'Rupert Grint', 'Michael Gambon']
Better?
import deepFreeze from 'deep-freeze';
const HPActors = deepFreeze(['Richard Harris', 'Daniel Radcliff', 'Emma Watson', 'Rupert Grint']);
PoAActors = deepFreeze(HPActors
.slice(1) //['Daniel Radcliff', 'Emma Watson', 'Rupert Grint']
.concat('Michael Gambon'))
//['Daniel Radcliff', 'Emma Watson', 'Rupert Grint', 'Michael Gambon']
Betterer?
Gross
Uh, Now what?
const crewStatus = { Zoe: 4, Wash: 4, Kaylee: 4, Jayne: 2 };
delete crewStatus.Jayne; //You betray one of my crew, you betray me.
import deepFreeze from 'deep-freeze';
const crewStatus = deepFreeze({
Zoe: 4, Wash: 4, Kaylee: 4, Jayne: 2
});
delete crewStatus.Jayne; //false
crewStatus; // {Zoe: 4, Wash: 4, Kaylee: 4, Jayne: 2}
DrΛwbΛcks
- Every time an object is made, it must also be frozen
- This is really only enforceable via a library
- Most of it's value is in development, not production runtime.
- No obvious way of producing an new object sans a given property/ies
IMMUTΛBLES Tip #3
LiterΛlly SpreΛd The Rest Out
const HPActors = ['Richard Harris', 'Daniel Radcliff', 'Emma Watson', 'Rupert Grint'];
PoAActors = HPActors
.slice(3, 1) //['Daniel Radcliff', 'Emma Watson', 'Rupert Grint']
.concat('Michael Gambon')
//['Daniel Radcliff', 'Emma Watson', 'Rupert Grint', 'Michael Gambon']
const HPActors = ['Richard Harris', 'Daniel Radcliff', 'Emma Watson', 'Rupert Grint'];
const [ origDumbledor, ...trio] = HPActors;
const PoAActors = ['Michael Gambon', ...trio];
//['Michael Gambon', 'Daniel Radcliff', 'Emma Watson', 'Rupert Grint']
Good
Better
const crewStatus = {
Zoe: 4, Wash: 4,
Kaylee: 4, Jayne: 0
};
delete crewStatus.Jayne; //You betray one of my crew, you betray me.
const crewStatus = {
Zoe: 4, Wash: 4,
Kaylee: 4, Jayne: 0
};
const {Jayne, ...newCrew} = crewStatus; // Next time you decide to stab me in the back...
newCrew; // { Zoe: 4, Wash: 4, Kaylee: 4 }
const oldCrew = {
...newCrew, Jayne: 1 // ...have the guts to do it to my face.
};
oldCrew; // { Zoe: 4, Wash: 4, Kaylee: 4, Jayne: 1 }
Mutanous
Extraction
Composition
IMMUTΛBLES Tip #3.5
You MΛy Not Need Tip #2
These λre not the inefficiencies you're looking for
while your first question mΛy be the most pertinent, you mΛy or mΛy not reΛlize it is Λlso the most irrelevΛnt
While I may concede your fancy performance test showing `.map` to be slower than a `for` loop, I also declare it to be completely irrelevant.
Your mΛchine is wΛiting orders of mΛgnitude longer thΛn it is processing.
I can assure you, your "proof" of a two fold (let alone the far more likely fractional) performance gain of a `for` loop over `.map` will not save you from latency of the miles of copper between request and response, the relative slow-motion rate of a disk read or write, nor the sloth's pace of a paint to the screen.
I/O is your performance bottle neck. Not your use of `.map`.
I tΛke it bΛck, I Do Reject your so cΛlled "proof"
JavaScript engines have seen incredible performance gains during that time.
JIT compilers alone have all but negated any slow code paths with their ability to optimize "hot" code on the fly.
Engines have tended to optimize on common patterns, and functional style JavaScript is only getting started.
Good luck finding any such data for JavaScript engines done in the last two years.
In fact, the spec calls specifically for native functions like `map`, etc. to implement tail call.
FΛvor MΛintenance over PerformΛnce
Don't misunderstand, performance is important, but when faced with competing interests, favor maintenance over performance.
Real, measurable money can be tied to maintenance, and that is going to be the first place to optimize.
Use MΛintΛinΛble PΛtterns, Not just FΛmiliΛr PΛtterns
Maintainable patterns are not the same as familiar patterns. Maintainability is a quality of the code itself, not just a function of a developer's or team's experience.
Maintainable code is robust, focused and has the right level of abstraction.
FΛvor DeclΛrΛtive ΛpproΛches over imperΛtive ones.
Declarative code is generally more maintainable and bug free than imperative code, even when the imperative approach is the more familiar.
A `.map` beats a `for...` loop for maintenance and likelihood of buglessness...
Every
Single
Time
A particular Thank you to my kind Reviewers who are generally much smarter than I.
- Zack Smith (@zacksmith5)
- Daniel Sellers (@daniel_sellers)
Functional Style JS - UtahJS 2016
By Cory Brown
Functional Style JS - UtahJS 2016
- 1,985