functional programming in javascript by luis atencio

64

Click here to load reader

Upload: luis-atencio

Post on 14-Apr-2017

2.494 views

Category:

Software


4 download

TRANSCRIPT

Page 1: Functional Programming in JavaScript by Luis Atencio

Thinking FunctionallyFunctional Programming using JavaScript

Luis AtencioBlog: http://luisatencio.netTwitter: @luijar

Page 2: Functional Programming in JavaScript by Luis Atencio

Functional Programming in JavaScriptwww.manning.com/atencio

Page 3: Functional Programming in JavaScript by Luis Atencio

Outline• Thinking functionally

– What and why– Paradigm shift– FP vs OO

• Get functional– Declarative programming– Side effects and referential transparency– Currying– Composition

• Lift your functional skills– Memoization– Monads

Page 4: Functional Programming in JavaScript by Luis Atencio

What is it?

“Functional programming refers to the declarative evaluation of pure functions to

create immutable programs by avoiding externally observable side effects.”

Page 5: Functional Programming in JavaScript by Luis Atencio

Why?

• Reduce complexity• Create code that is easier to trace, debug, and

test• Modularize code• Avoid duplications• Implement changes unobtrusively• Create code that is extensible and configurable• …

Page 6: Functional Programming in JavaScript by Luis Atencio

6

Paradigm shift

• Eliminate externally observable side effects• Control (reduce or eliminate) mutations• Write declaratively and point-free• Everything is a value (even functions)• Recursion as looping mechanism (eliminate

loops)• Functions ALWAYS return values

Page 7: Functional Programming in JavaScript by Luis Atencio

Is JavaScript functional?JavaScript is a dynamic, object-oriented programing language whose expressive power via closures and high-order functions makes it compelling for writing in an functional style

ES6 features that favor functional programming:• const keyword• Promises• Lambda expressions• Generators and Iterators

Page 8: Functional Programming in JavaScript by Luis Atencio

FP JavaScript EcosystemJavaScript has many libraries that implement many functional programming techniques:• Ramda.js http://ramdajs.com/0.17/index.html• Lodash.js https://lodash.com/• Underscore.js http://underscorejs.org/• Lazy.js -> http://danieltao.com/lazy.js/• Wu.js https://fitzgen.github.io/wu.js/• Fn.js http://eliperelman.com/fn.js/

Page 9: Functional Programming in JavaScript by Luis Atencio

9

Functional Programming in JS

• High-Order functions– Functions in JS can be used as parameters, assigned to

variables, and returned from other functions (lead to LSP)• Closures

<< outer scope (global) >>function makeInner(params) {

<< inner scope >> return function inner(params2) { << function body >> } var additionalVars;}

Page 10: Functional Programming in JavaScript by Luis Atencio

10

What about OO?

Page 11: Functional Programming in JavaScript by Luis Atencio

Battle of the Hello World!

document.getElementById(’msg').innerHTML = '<h1>Hello World</h1>';

compose(addToDom(’msg'), h1, echo('Hello World'));

vs

Page 12: Functional Programming in JavaScript by Luis Atencio

More functional Hello World!

compose ( addToDom(’msg'),

h2, repeat(3), echo('Hello World'));

configurable

Page 13: Functional Programming in JavaScript by Luis Atencio

Declarative Programming• Describe WHAT a program does• Not HOW to do it

SQL> SELECT p.firstname, p.birthYear FROM Person p WHERE p.birthYear > 1903 AND p.country = 'US' GROUP BY p.firstname, p.birthYear

Page 14: Functional Programming in JavaScript by Luis Atencio

Get FunctionalFunctional Techniques

Page 15: Functional Programming in JavaScript by Luis Atencio

function addToTable(personId) { if(personId != null) { personId = studentId.replace(/^\s*|\-|\s*$/g, ''); if(personId.length !== 9) { throw new Error('Invalid Input'); } var person = store.get(personId); if (person) { var rowInfo = `<td>${person.ssn}</td> <td>${person.firstname}</td> <td>${person.lastname}</td>`;

$(`\#${tableId} tr:last`).after(`<tr>${rowInfo}</tr>`); return $(`\#${tableId} tr`).length - 1; } else { throw new Error(’Person Record not found!'); } } else { return 0; }}

Fromimperative

Side effects

Page 16: Functional Programming in JavaScript by Luis Atencio

… after thinking functionally…

Page 17: Functional Programming in JavaScript by Luis Atencio

17

To functionalvar addToTable = compose( appendToTable(’personTable'),

populateRow, props(['ssn', 'firstname', 'lastname']),

findPerson, normalize, trim);

Page 18: Functional Programming in JavaScript by Luis Atencio

Things to understand

• The issue of side effects• Referential Transparency• Singularity principle• Liskov Substitution Principle• Currying and composition• Functors and Monads

Page 19: Functional Programming in JavaScript by Luis Atencio

Side effects• Changing a variable, property or data structure globally• Changing the original value of a function’s argument• Processing user input• Throwing an exception, unless it’s caught within the same

function• Printing to the screen or logging• Querying the DOM or databases

Page 20: Functional Programming in JavaScript by Luis Atencio

Forget about stateful functions?

• Date.now()

• Math.random()

• Array.sort()

• console.log()

Page 21: Functional Programming in JavaScript by Luis Atencio

How do we deal with change?

• Simply don’t change any objects…. (right)• Use const (limited)• Create Value Objects (only in certain cases)• JavaScript’s Object.freeze (shallow)• Use Lenses (more elegant option!)

Page 22: Functional Programming in JavaScript by Luis Atencio

Lenses var person = new Person('Alonzo', 'Church'); var lastnameLens = lenseProp('lastName'); view(lastnameLens, person); //-> 'Church' var newPerson = set(lastnameLens, 'Mourning', person);newPerson.lastname; //-> 'Mourning’person.lastname; //-> 'Church'

var person = { firstname:'Alonzo’, lastname: 'Church'}

Page 23: Functional Programming in JavaScript by Luis Atencio

Understanding referential transparency

• Functions behave like mathematical functions var increment = (val) => val + 1; • Functions are relations that map a set of types to other types• Functions shall return the same output on same input

• Functions require all parameters needed to perform its work

N

N

N

N

Nincrement

domain range

Page 24: Functional Programming in JavaScript by Luis Atencio

Equational Reasoning

var input = [80, 90, 100];

divide(sum(input), size(input)); //-> 90

divide(270, 3); //-> 90

270 / 3 = 90

Input -> Program = [func1, func2, func3, ...] -> Output

Page 25: Functional Programming in JavaScript by Luis Atencio

Simple Functions• Singularity principle: functions are supposed

to do perform only one task• Simple functions typically have fewer

arguments (reduced arity) that complex functions

• Simple functions are easy to compose and chain

Page 26: Functional Programming in JavaScript by Luis Atencio

Bye Bye Loops• Loops introduce non-linear program flow• Loops mutate data (variable counter)• They can be modeled with functions

var acc = 0;for (let i = 0; i < nums.length; i++) { acc += nums[i];}

function sum(arr) { var list = _(arr); return list.isEmpty() ? 0 : return list.head() + sum(list.tail());}

Page 27: Functional Programming in JavaScript by Luis Atencio

Lazy Function Chains• Other alternatives to loops• Use high level constructs such as map, reduce, and

filter• Functional libraries implement clever techniques like– Pipelining– Method fusion

var fruits = ['Apple', 'apple', 'ORANGE', 'banana', 'bAnAnA']result = chain(fruits) .map(startCase) .uniq() .sort() .value(); //-> ['Apple', 'Banana','Orange']

Page 28: Functional Programming in JavaScript by Luis Atencio

Liskov Substitution Principle

• Functions that use references to base classes must be able to use objects of derived classes without knowing it

• Programming functionally with objects means separating the state from its behavior

• Beneficial for building function chains and pipelines• Practically, this means avoiding the use of this

Page 29: Functional Programming in JavaScript by Luis Atencio

fullname(person)

Person

get fullname()

Student

get school()

Person

Student

var person = new Person('Alonzo', 'Church', '444-44-4444');p.fullname(); //-> Alonzo Church

var fullname = (person) => [person.firstname, person.lastname].join(' ');

fullname(person); //-> Alonzo Church

Uses the this reference to access object’s data

Eliminates the use of this since object is supplied as parameter

FP separates methods into high-order function that can work on instances of base type Person, which must also work with Student

Page 30: Functional Programming in JavaScript by Luis Atencio

Currying• Some functions can’t be reduced to single arguments• Used to partially evaluate a function as a sequence of

steps by providing arguments one-at-a-time• Currying enables the composition of complex

functions

function f(a, b, c) { … }

f a f(a, undefined, undefined)

evaluating:

returns:

Page 31: Functional Programming in JavaScript by Luis Atencio

Currying2

function f(a, b, c) { … } curry(f) :: (a,b,c) -> f(a) -> f(b) -> f(c)

f(a, b, c) { return function (a) { return function (b) { return function (c) { … } } }

f a f(b, c)

evaluating:

f a f(c)b

f a resultb c

returns:

Page 32: Functional Programming in JavaScript by Luis Atencio

Currying3

var name = curry2(function (last, first) { return [last, first].join(',');}); name('Curry')('Haskell'); //-> 'Curry, Haskell’ name('Curry'); //-> Function

Page 33: Functional Programming in JavaScript by Luis Atencio

Composition• Deep-link a function’s

return value with another function’s arguments

• Separates a program’s description from evaluation

• Composition leads to point-free programs

A

A

A

B

B C

Cgf

f o g = f(g(x))

• The resulf of composing a function is another function that can be composed further

Page 34: Functional Programming in JavaScript by Luis Atencio

Composition2

f o g = f(g) = compose :: (B -> C) -> (A -> B) -> (A -> C)

var str = `A complex system that works is invariably found to have evolved from a simple system that worked `;

var explode = (str) => str.split(/\s+/); var count = (arr) => arr.length; var countWords = compose(count, explode); countWords(str); // -> 17

Page 35: Functional Programming in JavaScript by Luis Atencio

function addToRoster(personId) { if(personId!== null) { personId = personId.replace(/^\s*|\-|\s*$/g, ''); if(personId.length !== 9) { throw new Error('Invalid Input'); }

var person = db.find(personId); if (person !== null) { var rowInfo =

`<td>${person.ssn}</td> <td>${person.firstname}</td> <td>${person.lastname}</td>`;

$(`\#${tableId} tr:last`).after( `<tr>${rowInfo}</tr>`); return $(`\#${tableId} tr`).length – 1; } else { throw new Error(’Person Record not found!'); } } else { return 0; }}

Breaking monolithic functions

Page 36: Functional Programming in JavaScript by Luis Atencio

addToTable

cleanInputcheckLengthSsn

findPerson

populateRow

appendToTable

Impure:can’t be tested

reliably

!Decompose = Compose

Become the building blocks of your program

Page 37: Functional Programming in JavaScript by Luis Atencio

Building blocksvar safeFindObject = curry(function (db, id) { return Maybe.fromNullable(find(db, id));});

var findPerson = safeFindObject(DB(’people'));

var trim = (str) => str.replace(/^\s*|\s*$/g, '');

var normalize = (str) => str.replace(/\-/g, '');

Page 38: Functional Programming in JavaScript by Luis Atencio

Building blocks2var populateRow = function (columns) { var cell_t = _.template('<td><%= a %></td>'); var row_t = _.template('<tr><%= a %></tr>'); var obj = function (a) { return {'a': a}; }; var row = compose(row_t, obj, R.join(''), map(cell_t), map(obj)); return row(columns);};

var addToTable = curry( function (elementId, rowInfo) { $(`#${elementId} tr:last`).after(`<tr>${rowInfo}</tr>`);

return $(`#${elementId} tr`).length - 1; });

Page 39: Functional Programming in JavaScript by Luis Atencio

39

Composedvar addToTable = compose( appendToTable(’personTable'),

populateRow, props(['ssn', 'firstname', 'lastname']),

findPerson, normalize, trim);

Page 40: Functional Programming in JavaScript by Luis Atencio

40

Composedvar addToTable = compose( appendToTable(’personTable'),

populateRow, props(['ssn', 'firstname', 'lastname']),

findPerson, normalize, trim);

Page 41: Functional Programming in JavaScript by Luis Atencio

Lift Functional SkillsFunctional Design Patterns

Page 42: Functional Programming in JavaScript by Luis Atencio

Memoization• Optimization technique used to avoid

unnecessary invocation of a computationally expensive function

• Based on the principle of referential transparency

• Only applies to pure functions• Implemented using a simple caching layer

Page 43: Functional Programming in JavaScript by Luis Atencio

43

Memoization2Function.prototype.memoized = function () { var key = JSON.stringify(arguments); this._cache = this._cache || {}; this._cache[key] = this._cache[key] || this.apply(this, arguments); return this._cache[key];};

Function.prototype.memoize = function () { var fn = this; if (fn.length === 0 || fn.length > 1) { return fn; } return function () { return fn.memoized.apply(fn, arguments); };};

Page 44: Functional Programming in JavaScript by Luis Atencio

44

var md5 = (function (str) { // algorithm details here... return digest; }).memoize();

var str = ’OO in the large, functional in the small’;md5(str); // 0.733 msmd5(str); // second time: 0.021 ms

Memoization3

Page 45: Functional Programming in JavaScript by Luis Atencio

md5

_cache'Get Functional!'

check cache

function key value

md5 Get Functional!

96d18935a41d37a54d60ce997675cc91

put

function key value

[empty]

contains

false

memoized

96d18935a41d37a54d60ce997675cc91

run function

96d18...96d18935a41d37a5...

'Get Functional!' check cache

contains

true

get

96d18...96d18935a41d37a54d60ce997675cc91

First call

Second call

Page 46: Functional Programming in JavaScript by Luis Atencio

46

Containerizingvar Wrapper = function (val) { this._val = val; }; // MapWrapper.prototype.map = function (f) { return f(this._val); }; // Unitvar wrap = (val) => new Wrapper(val);

guarded

identity

map

identity returnsthe same value

Wrapper

Page 47: Functional Programming in JavaScript by Luis Atencio

Containerizing2

var wrappedValue = wrap('Get Functional'); // extract the valuevar value = wrappedValue.map(toUpper).map(repeat(2)).map(identity);

value; //-> 'GET FUNCTIONAL GET FUNCTIONAL'

Page 48: Functional Programming in JavaScript by Luis Atencio

Functors: next level containers

48

// MapWrapper.prototype.map = function (f) { return f(this.val); };

// FunctorWrapper.prototype.fmap = function (f) { return wrap(f(this.val));};

• Data structure that can be mapped over

• Lift values into a container so that you can apply functions onto them, place the result back into the container

Page 49: Functional Programming in JavaScript by Luis Atencio

49

Functors2

var plus = curry((a, b) => a + b);var plus3 = plus(3);var two = wrap(2);var five = two.fmap(plus3); //-> Wrapper(5) two.fmap(plus3).fmap(plus10); //-> Wrapper(15)

plus3

fmapWrapper

2

Wrapper

2

apply function

Wrapper

5

wrap

Page 50: Functional Programming in JavaScript by Luis Atencio

50

Why do this?

Wrapping a potentially null value or a function that can cause the program to fail

Page 51: Functional Programming in JavaScript by Luis Atencio

51

Software is unpredictable

Exception thrown but contained within the wrapper

Exception does not affect any other part of the system

Page 52: Functional Programming in JavaScript by Luis Atencio

52

Software must be robust

Function throws an exception

Exception is propagated and gracefully handled

Program flow

Page 53: Functional Programming in JavaScript by Luis Atencio

53

Monads

Functor + Unit = Monad

…and some more

Page 54: Functional Programming in JavaScript by Luis Atencio

54

Monads2• Backbone of functional

programming• Treat data and operations

algebraically• Data type used for applying a

sequence of transformations on data (conveyor belt model)

• Abstract data flows• Used for error handling, IO,

Logging, etc

Page 55: Functional Programming in JavaScript by Luis Atencio

55

Error handling and Maybe• Wall-off impurity• Consolidate null-check logic• Consolidated exception throwing• Support compositionally of functions• Centralize logic for providing default values

Page 56: Functional Programming in JavaScript by Luis Atencio

56

Maybe Monad2

Just

object

Nothing

Maybe

Just(value): represents a container that wraps a defined value.

Nothing(): represents a container that has no value, or a failure that needs no additional information.

Page 57: Functional Programming in JavaScript by Luis Atencio

57

Maybe Monad3

class Maybe { static fromNullable(a) { return a !== null ? just(a) : nothing(); }  static of(a) { return just(a); }}

class Just extends Maybe { map(f) { return of(f(this.value)); }  getOrElse() { return this.value; }}

Page 58: Functional Programming in JavaScript by Luis Atencio

58

Maybe Monad4class Nothing extends Maybe { map(f) { return this; // noop } get value() { throw new TypeError(`Can't extract the value of a Nothing.`); } getOrElse(other) { return other; } }

Page 59: Functional Programming in JavaScript by Luis Atencio

Maybe Example

59

Maybe.of(3).map(plus2); //-> Maybe(5)

Maybe.of(3).chain(plus2); //-> 5

Maybe.fromNullable(null).map(plus2); //-> Nothing()

Page 60: Functional Programming in JavaScript by Luis Atencio

Maybe Example2

60

function getCountry(student) { var school = student.school(); if (school !== null ) {

var addr = school.address();

if (addr !== null ) {return

addr.country(); }}return 'Country does not

exist!';}

student; //-> Maybe<Student>

var getCountry = (student) => student .map(prop('school')) .map(prop('address')) .map(prop('country'))

.getOrElse('Country does not exist!');

Page 61: Functional Programming in JavaScript by Luis Atencio

61

Monads abstract data flow

cleanInputSSN SSN checkLengthSsn SSN findPerson

addToTable(SSN)

nullpopulateRow

Leftnull

LeftappedToTable

orElse errorLog

skipped skipped

props

skipped

When an error occurs, Maybe safely propagatesthe error through the components of your code

Page 62: Functional Programming in JavaScript by Luis Atencio

To recap…

Page 63: Functional Programming in JavaScript by Luis Atencio

function addToTable(personId) { if(personId!= null) { personId= personId (/^\s*|\-|\s*$/g, ''); if(personId!== 9) { throw new Error('Invalid Input'); } var person= store.get(personId); if (person) { var rowInfo = `<td>${person.ssn}</td> <td>${person.firstname}</td> <td>${person.lastname}</td>`;

$(`\#${tableId} tr:last`).after(`<tr>${rowInfo}</tr>`); return $(`\#${tableId} tr`).length - 1; } else { throw new Error(’Person not found!'); } } else { return 0; }}

Fromimperative

Page 64: Functional Programming in JavaScript by Luis Atencio

64

To functionalvar addToTable = compose( appendToTable(’personTable'),

populateRow, props(['ssn', 'firstname', 'lastname']),

findPerson, normalize, trim);

• Eliminate side effects!• Reduce complexity!• Code is more testable!• Declarative and easier to read!• Reduce number of bugs!