Func up your code

Post on 08-Feb-2017

204 views 0 download

transcript

Func up your codeMaciej Komorowski

What is func%onal programming?

Func%onal programming is programming paradigm [...] that

treats computa%on as the evalua%on of mathema&cal func&ons and

avoids changing-state and mutable data.

— Wikipedia

Pillars of FP

• Higher-order func0ons (e.g. map)

• Immutable data

• Lazy evalua0on

• Pure func0ons

• Recursion

Pure func)on! Always evaluates the same result value given the same arguments! Does not cause any seman5cally observable side effect, such as muta5on of mutable objects

Pure func)on examples// Purefunction add(a, b) { return a + b;}

// ImpureMath.random();

// Impureconst items = []

function addItem(item) { return items.push(item);}

State muta(on

State muta(on in OOP

! Relies heavily on encapsula)on! Has hidden nature! Brings nondeterminism! Root cause of doom

Brogrammer1

State muta)on example

A brogrammer is [...] a slang term for a macho, male programmer. A brogrammer

might self-describe as a sociable programmer.

— Wikipedia

1 Frans Hals, Oil on canvas, 1630. Source: classicprogrammerpain;ngs

What brogrammer does?

• drinkBeer

• talk

Stateful implementa*on

class Brogrammer { constructor() { this.beers = 0; }

drinkBeer() { /* ... */ } talk() { /* ... */ }}

Stateful – drinkBeer

it('increment number of drunk beers', () => { expect(brogrammer.drinkBeer()).toEqual(1); expect(brogrammer.drinkBeer()).toEqual(2);});

// Inside Brogrammer classdrinkBeer() { return this.beers += 1; // encapsulates 'beers'}

Stateful – talk testsit('says "Beer me up!" before drinking', () => { expect(brogrammer.talk()).toEqual("Beer me up!");});

it('says "Yummy" after 1st beer', () => { brogrammer.drinkBeer(); expect(brogrammer.talk()).toEqual("Yummy");});

it('says "I a<M dRuNk" after 10th beer', () => { brogrammer.drinkBeer(); // 8 x brogrammer.drinkBeer(); brogrammer.drinkBeer(); expect(brogrammer.talk()).toEqual("I a<M dRuNk");});

Stateful – talk implementa+on

// Inside Brogrammer classtalk() { return ({ 0: 'Beer me up!', 1: 'Yummy', // ... 10: 'I a<M dRuNk', })[this.beers] || '';}

How to remove state?Single source of truth

Naïve stateless – implementa+on

class Brogrammer { constructor(store) { this.store = store; }

// ...}

Naïve stateless – drinkBeer tests

it('increment number of drunk beers', () => { const store = { beers: 0 }; const brogrammer = new Brogrammer(store);

brogrammer.drinkBeer();

expect(store.beers).toEqual(1);});

Naïve stateless – drinkBeer implementa+on

// Inside Brogrammer classdrinkBeer() { return this.store.beers += 1;}

Naïve stateless – talk tests

it('says "I a<M dRuNk" after 10th beer', () => { const store = { beers: 10 }; const brogrammer = new Brogrammer(store);

expect(brogrammer.talk()).toEqual("I a<M dRuNk");});

Naïve stateless – talk implementa+on

// Inside Brogrammer classtalk() { return ({ 0: 'Beer me up!', 1: 'Yummy', // ... 10: 'I a<M dRuNk', })[this.store.beers] || '';}

Solu%on review! Removed state from the Brogrammer! Easier tes2ng" Lost encapsula-on

How to improve?The Redux way

Redux workflow2

2 Source: CSS-Tricks

Three Principles of Redux3

! Single source of truth! State is read-only! Changes are made with pure func)ons

3 Source: Redux

Three Principles of ReduxSingle source of truth

console.log(store.getState());

// {// brogrammer: {// beers: 0// }// }

Three Principles of ReduxState is read-only

store.dispatch({ type: 'INCREMENT_BEERS',});

Three Principles of ReduxChanges are made with pure func3ons

function brogrammer(state = {}, action) { switch (action.type) { case 'SET_BEERS': return { ...state, beers: 5, }; // case ... }}

Redux – incrementBeers ac%on

it('returns action with type INCREMENT_BEERS', () => { const action = incrementBeers();

expect(action).toEqual({ type: 'INCREMENT_BEERS' });});

Redux – incrementBeers ac%on

const incrementBeers = () => ({ type: 'INCREMENT_BEERS',});

Redux – brogrammer reducer

it('returns state with incremented beers', () => { const state = { beers: 0 }; const action = incrementBeers();

const nextState = brogrammer(state, action);

expect(nextState).toEqual({ beers: 1 });});

Redux – brogrammer reducerimplementa)on

const brogrammer = (state = {}, action) => { switch (action.type) { case 'INCREMENT_BEERS': return { ...state, beers: state.beers + 1 }; default: return state }}

Redux – drinkBeer tests

it('increment number of drunk beers', () => { const store = createStore({ beers: 0 }); const brogrammer = new Brogrammer(store);

brogrammer.drinkBeer();

expect(store.dispatch) .toHaveBeenCalledWith(incrementBeers());});

Redux – drinkBeer implementa+on

// Inside Brogrammer classdrinkBeer() { return this.store.dispatch(incrementBeers());}

Redux – talk tests

it('says "I a<M dRuNk" after 10th beer', () => { const store = createStore({ beers: 10 }); const brogrammer = new Brogrammer(store);

expect(brogrammer.talk()).toEqual("I a<M dRuNk");});

Redux – talk implementa+on

// Inside Brogrammer classtalk() { return ({ 0: 'Beer me up!', 1: 'Yummy', // ... 10: 'I a<M dRuNk', })[this.store.get('beers')] || '';}

Solu%on review! Easy tes(ng! Be,er separa(on! Explicit state changes! Full control over state

A few thoughts on Redux

Immutable data

Mutable vs immutable// Mutableconst items = ['foo'];

items.push('bar');items.push('baz');

console.log(items); // ["foo", "bar", "baz"]

Mutable vs immutable// Immutable via immutable-jsconst items = Immutable.List.of('foo');

items.push('bar');items.push('baz');

console.log(items.toArray());// ["foo"]

console.log(items.push('bar').push('baz').toArray());// ["foo", "bar", "baz"]

What about performance?5

5 Source: React.js Conf 2015 - Immutable Data and React

Performance benchmarkArray push 1,000,000 items:

var list = [];for (var i = 0; i < 1000000; i++) { list.push(i);}

83 ms

Performance benchmarkMori immutable vector conj 1,000,000 items:

var list = mori.vector();for (var i = 0; i < 1000000; i++) { mori.conj(list, i);}

288 ms

Should update problemconst form = { /* ... */ };

function submit(form) { if (hasChanged(form)) { doSomethingExpensive(form); }}

Should update problemversioning

let currentFormVersion;

function hasChanged(form) { const formVersion = md5(JSON.stringify(form));

return formVersion !== currentFormVersion;}

Should update problemdirty bits

function hasChanged(form) { for (var field in form) { if (field.meta.dirty === true) { return true; } }

return false;}

Should update problemobservable pa+ern

const form = { /* ... */ };

Object.observe(form, changes => { doSomethingExpensive(form);});

form.firstName = 'Maciej';form.lastName = 'Komorowski';form.profession = 'Brogrammer'; // Not true :)

Should update problemimmutable

let currentForm = { /* ... */ };

function hasChanged(form) { return currentForm !== form;}

Memoiza(on examplefunction memoize(fn) { var cache = {}; return function (arg) { var hash = arg === Object(arg) ? JSON.stringify(arg) // Wat? : arg; return hash in cache ? cache[hash] : (cache[hash] = fn.call(this, arg)); }}

Memoiza(on examplefunction rawSum(list) { return list.reduce((a, b) => a + b)}

const sum = memoize(rawSum);const array = [0, 1, ...1000000];

sum(array); // 89 ms

rawSum(array); // 51 mssum(array); // 42 ms

Advantages of immutability! No defensive copies! Can be faster! Be3er for concurrency (no deadlocks)

Explore func-onal programing❗It's worth it

ContactMaciej Komorowski

@komomc

QuizWhat f does?

f = 0 : 1 : zipWith (+) f (tail f)

AnswerGenerates Fibonacci sequence

> let fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

> take 11 fibs[0,1,1,2,3,5,8,13,21,34,55]