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

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

  • 2,014