Thinking Functionally Functional Programming using JavaScript Luis Atencio Blog: http://luisatencio.net Twitter: @luijar
Functional Programming in JavaScript www.manning.com/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
What is it? “Functional programming refers to the declarative evaluation of pure functions to create immutable programs by avoiding externally observable side effects.”
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 • …
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 6
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
FP JavaScript Ecosystem JavaScript 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/
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 9 << outer scope (global) >> function makeInner(params) { << inner scope >> return function inner(params2) { << function body >> } var additionalVars; }
What about OO? 10
Battle of the Hello World! document.getElementById(’msg').innerHTML = '<h1>Hello World</h1>'; compose(addToDom(’msg'), h1, echo('Hello World')); vs
More functional Hello World! compose ( addToDom(’msg'), h2, repeat(3), echo('Hello World')); configurable
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
Get Functional Functional Techniques
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; } } From imperative Side effects
… after thinking functionally…
To functional var addToTable = compose( appendToTable(’personTable'), populateRow, props(['ssn', 'firstname', 'lastname']), findPerson, normalize, trim); 17
Things to understand • The issue of side effects • Referential Transparency • Singularity principle • Liskov Substitution Principle • Currying and composition • Functors and Monads
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
Forget about stateful functions? • Date.now() • Math.random() • Array.sort() • console.log()
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!)
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' }
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 N increment domain range
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
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
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()); }
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']
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
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
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 :
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:
Currying3 var name = curry2(function (last, first) { return [last, first].join(','); }); name('Curry')('Haskell'); //-> 'Curry, Haskell’ name('Curry'); //-> Function
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 Cg f f o g = f(g(x)) • The resulf of composing a function is another function that can be composed further
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
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
addToTable cleanInput checkLengthSsn findPerson populateRow appendToTable ✔ ✔ ✔ Impure: can’t be tested reliably  !Decompose = Compose Become the building blocks of your program
Building blocks var 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, '');
Building blocks2 var 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; });
Composed var addToTable = compose( appendToTable(’personTable'), populateRow, props(['ssn', 'firstname', 'lastname']), findPerson, normalize, trim); 39
Composed var addToTable = compose( appendToTable(’personTable'), populateRow, props(['ssn', 'firstname', 'lastname']), findPerson, normalize, trim); 40
Lift Functional Skills Functional Design Patterns
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
Memoization2 43 Function.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); }; };
var md5 = (function (str) { // algorithm details here... return digest; }).memoize(); var str = ’OO in the large, functional in the small’; md5(str); // 0.733 ms md5(str); // second time: 0.021 ms 44 Memoization3
md5 _cache 'Get Functional!' check cache function key value md5 Get Functional! 96d18935a41d37a54d60ce9976 75cc91 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
Containerizing 46 var Wrapper = function (val) { this._val = val; }; // Map Wrapper.prototype.map = function (f) { return f(this._val); }; // Unit var wrap = (val) => new Wrapper(val); guarded identity map identity returns the same value Wrapper
Containerizing2 var wrappedValue = wrap('Get Functional'); // extract the value var value = wrappedValue.map(toUpper).map(repeat(2)).map(identity); value; //-> 'GET FUNCTIONAL GET FUNCTIONAL'
Functors: next level containers 48 // Map Wrapper.prototype.map = function (f) { return f(this.val); }; // Functor Wrapper.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
Functors2 49 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 fmap Wrapper 2 Wrapper 2 apply function Wrapper 5 wrap
Why do this? 50 Wrapping a potentially null value or a function that can cause the program to fail
Software is unpredictable 51 Exception thrown but contained within the wrapper Exception does not affect any other part of the system
Software must be robust 52 Function throws an exception Exception is propagated and gracefully handled Program flow
Monads 53 Functor + Unit = Monad …and some more
Monads2 54 • 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
Error handling and Maybe 55 • Wall-off impurity • Consolidate null-check logic • Consolidated exception throwing • Support compositionally of functions • Centralize logic for providing default values
Maybe Monad2 56 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.
Maybe Monad3 57 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; } }
Maybe Monad4 58 class 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; } }
Maybe Example 59 Maybe.of(3).map(plus2); //-> Maybe(5) Maybe.of(3).chain(plus2); //-> 5 Maybe.fromNullable(null).map(plus2); //-> Nothing()
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!');
Monads abstract data flow 61 cleanInputSSN SSN checkLengthSsn SSN findPerson addToTable(SSN) nullpopulateRow Left null Left appedToTable orElse errorLog skipped skipped props skipped When an error occurs, Maybe safely propagates the error through the components of your code
To recap…
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; From imperative
To functional var addToTable = compose( appendToTable(’personTable'), populateRow, props(['ssn', 'firstname', 'lastname']), findPerson, normalize, trim); 64 • Eliminate side effects! • Reduce complexity! • Code is more testable! • Declarative and easier to read! • Reduce number of bugs!

Functional Programming in JavaScript by Luis Atencio

  • 1.
    Thinking Functionally Functional Programmingusing JavaScript Luis Atencio Blog: http://luisatencio.net Twitter: @luijar
  • 2.
  • 3.
    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
  • 4.
    What is it? “Functionalprogramming refers to the declarative evaluation of pure functions to create immutable programs by avoiding externally observable side effects.”
  • 5.
    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 • …
  • 6.
    Paradigm shift • Eliminateexternally 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 6
  • 7.
    Is JavaScript functional? JavaScriptis 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
  • 8.
    FP JavaScript Ecosystem JavaScripthas 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/
  • 9.
    Functional Programming inJS • High-Order functions – Functions in JS can be used as parameters, assigned to variables, and returned from other functions (lead to LSP) • Closures 9 << outer scope (global) >> function makeInner(params) { << inner scope >> return function inner(params2) { << function body >> } var additionalVars; }
  • 10.
  • 11.
    Battle of theHello World! document.getElementById(’msg').innerHTML = '<h1>Hello World</h1>'; compose(addToDom(’msg'), h1, echo('Hello World')); vs
  • 12.
    More functional HelloWorld! compose ( addToDom(’msg'), h2, repeat(3), echo('Hello World')); configurable
  • 13.
    Declarative Programming • DescribeWHAT 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
  • 14.
  • 15.
    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; } } From imperative Side effects
  • 16.
    … after thinkingfunctionally…
  • 17.
    To functional var addToTable= compose( appendToTable(’personTable'), populateRow, props(['ssn', 'firstname', 'lastname']), findPerson, normalize, trim); 17
  • 18.
    Things to understand •The issue of side effects • Referential Transparency • Singularity principle • Liskov Substitution Principle • Currying and composition • Functors and Monads
  • 19.
    Side effects • Changinga 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
  • 20.
    Forget about statefulfunctions? • Date.now() • Math.random() • Array.sort() • console.log()
  • 21.
    How do wedeal 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!)
  • 22.
    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' }
  • 23.
    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 N increment domain range
  • 24.
    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
  • 25.
    Simple Functions • Singularityprinciple: 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
  • 26.
    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()); }
  • 27.
    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']
  • 28.
    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
  • 29.
    fullname(person ) Person get fullname() Student get school() Person Student varperson = 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
  • 30.
    Currying • Some functionscan’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 :
  • 31.
    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:
  • 32.
    Currying3 var name =curry2(function (last, first) { return [last, first].join(','); }); name('Curry')('Haskell'); //-> 'Curry, Haskell’ name('Curry'); //-> Function
  • 33.
    Composition • Deep-link afunction’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 Cg f f o g = f(g(x)) • The resulf of composing a function is another function that can be composed further
  • 34.
    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
  • 35.
    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
  • 36.
  • 37.
    Building blocks var 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, '');
  • 38.
    Building blocks2 var 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; });
  • 39.
    Composed var addToTable =compose( appendToTable(’personTable'), populateRow, props(['ssn', 'firstname', 'lastname']), findPerson, normalize, trim); 39
  • 40.
    Composed var addToTable =compose( appendToTable(’personTable'), populateRow, props(['ssn', 'firstname', 'lastname']), findPerson, normalize, trim); 40
  • 41.
  • 42.
    Memoization • Optimization techniqueused 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
  • 43.
    Memoization2 43 Function.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); }; };
  • 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 ms md5(str); // second time: 0.021 ms 44 Memoization3
  • 45.
    md5 _cache 'Get Functional!' check cache functionkey value md5 Get Functional! 96d18935a41d37a54d60ce9976 75cc91 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
  • 46.
    Containerizing 46 var Wrapper =function (val) { this._val = val; }; // Map Wrapper.prototype.map = function (f) { return f(this._val); }; // Unit var wrap = (val) => new Wrapper(val); guarded identity map identity returns the same value Wrapper
  • 47.
    Containerizing2 var wrappedValue =wrap('Get Functional'); // extract the value var value = wrappedValue.map(toUpper).map(repeat(2)).map(identity); value; //-> 'GET FUNCTIONAL GET FUNCTIONAL'
  • 48.
    Functors: next levelcontainers 48 // Map Wrapper.prototype.map = function (f) { return f(this.val); }; // Functor Wrapper.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
  • 49.
    Functors2 49 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 fmap Wrapper 2 Wrapper 2 apply function Wrapper 5 wrap
  • 50.
    Why do this? 50 Wrappinga potentially null value or a function that can cause the program to fail
  • 51.
    Software is unpredictable 51 Exceptionthrown but contained within the wrapper Exception does not affect any other part of the system
  • 52.
    Software must berobust 52 Function throws an exception Exception is propagated and gracefully handled Program flow
  • 53.
    Monads 53 Functor + Unit= Monad …and some more
  • 54.
    Monads2 54 • Backbone offunctional 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
  • 55.
    Error handling andMaybe 55 • Wall-off impurity • Consolidate null-check logic • Consolidated exception throwing • Support compositionally of functions • Centralize logic for providing default values
  • 56.
    Maybe Monad2 56 Just object Nothing Maybe Just(value): representsa container that wraps a defined value. Nothing(): represents a container that has no value, or a failure that needs no additional information.
  • 57.
    Maybe Monad3 57 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; } }
  • 58.
    Maybe Monad4 58 class Nothingextends Maybe { map(f) { return this; // noop } get value() { throw new TypeError(`Can't extract the value of a Nothing.`); } getOrElse(other) { return other; } }
  • 59.
    Maybe Example 59 Maybe.of(3).map(plus2); //->Maybe(5) Maybe.of(3).chain(plus2); //-> 5 Maybe.fromNullable(null).map(plus2); //-> Nothing()
  • 60.
    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!');
  • 61.
    Monads abstract dataflow 61 cleanInputSSN SSN checkLengthSsn SSN findPerson addToTable(SSN) nullpopulateRow Left null Left appedToTable orElse errorLog skipped skipped props skipped When an error occurs, Maybe safely propagates the error through the components of your code
  • 62.
  • 63.
    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; From imperative
  • 64.
    To functional var addToTable= compose( appendToTable(’personTable'), populateRow, props(['ssn', 'firstname', 'lastname']), findPerson, normalize, trim); 64 • Eliminate side effects! • Reduce complexity! • Code is more testable! • Declarative and easier to read! • Reduce number of bugs!