From e81205e245a6c71de4eeef8208beee64141c6690 Mon Sep 17 00:00:00 2001 From: Francis Murillo Date: Fri, 15 Apr 2016 18:32:23 +0800 Subject: [PATCH] Updating with my intended presentation --- presentation-shortened.html | 9108 +++++++++++++++++++++++++++++++++++ presentation-shortened.org | 1999 ++++++++ presentation.html | 1872 +++++-- presentation.org | 1533 +++++- 4 files changed, 13884 insertions(+), 628 deletions(-) create mode 100644 presentation-shortened.html create mode 100644 presentation-shortened.org diff --git a/presentation-shortened.html b/presentation-shortened.html new file mode 100644 index 0000000..0e0ea6e --- /dev/null +++ b/presentation-shortened.html @@ -0,0 +1,9108 @@ + + + + +The Power Of Functions In JavaScript + + + + + + +
+
+ +
+
+

The Power Of Functions In JavaScript

+

+By Francis Murillo +

+ + +

+On Apr. 14, 2016 +

+ + +

+At Manila, JavaScript #06 +

+ + +

+francisavmurillo@gmail.com +

+ + +

+https://github.com/FrancisMurillo/the-power-of-functions-in-javascript +

+ + +
+
+
+
+

Foolish Assumptions

+
    +
  • You know the basics of JavaScript syntax
  • +
  • I am talking about JavaScript in the browser
  • +
  • You and I hate presentations that waste time
  • + +
+ +
+
+
+
+

A Little Background

+

+Just a mediocre software developer. +

+ +
+
+
+
+

Before We Begin

+
+

+Judge a man by his questions rather than his answers +

+ +

+– Voltaire +

+
+ +

+Who here thinks JavaScript (in the browser) sucks? +

+ +
+
+

JavaScript Sucks

+

+Where do I begin? +

+ +
    +
  • Lousy type system
  • +
  • No module system
  • +
  • Global by default
  • +
  • No block scope
  • +
  • Monkey patching
  • +
  • The rest couldn't fit on one slide
  • + +
+ +

+But I am not here to bash on the language. +

+ +

+How do we write good code in a (sucky but awesome) language? +

+ +
+
+

Writing Good Code For JavaScript

+

+How do write code that is… +

+ +
    +
  • Modular
  • +
  • Extensible
  • +
  • Reusable
  • +
  • Readable
  • +
  • Poetry
  • + +
+ +

+JavaScript already has a concept for that +

+ +

+It's not a framework, or a library or a new concept and it's been around +since ancient times +

+ +
+
+

Function

+

+The building blocks of programs +

+ +
+ +
var theAllPowerful = function () {
+    return 'Bow before me';
+}
+
+
+ +
+
+
+
+

Functions In JavaScript

+
+

+The best thing about JavaScript is its implementation of functions. It +got almost everything right. But, as you should expect with +JavaScript, it didn’t get everything right. +

+ +

+– Douglas Crockford +

+
+ +

+In order to understand their supremacy, let's review what functions look like +in JavaScript first. +

+ +
+
+

Things You Should Know

+

+Just a brief review of the following. +

+ +
    +
  • Functions
  • +
  • arguments keyword
  • +
  • call and apply function
  • +
  • Closure / Function Scope
  • + +
+ +
+
+
+
+

Functions

+

+The bread and butter of programmers +

+ +
+
+

Declaring Functions

+

+There are two primary ways to declare a function. +

+ +
+ +
// Function statement
+function greetLucy() {
+    return 'Hello Lucile';
+}
+
+// Function expression
+var greetPatty = function _greetPatty() {
+    return 'Hello Sir';
+};
+
+
+ +
+
+

Invoking Functions

+

+There's three known ways to call or invoke a function. +

+ +
+ +
// Math yo
+var add = function _doTheMath(x, y) {
+    return x + y;
+};
+
+// The canonical function invokation
+add(1, 2);
+
+// The functional way of calling functions
+// We'll get to this later
+add.call(null, 1, 2);
+
+// Same thing above but the arguments is passed as an array
+add.apply(null, [1, 2]);
+
+
+ +
+
+

Returning An Output

+

+return is the output of the function. If there is no return, an +implicit undefined is the output. +

+ +
+ +
function returnSomething(x) {
+    return 'I call ' + x.toString();
+};
+
+var noReturn = function () {
+    // do something
+}
+
+console.log(returnSomething('Pikachu')); // out: 'I call Pikachu'
+console.log(noReturn()); // out: undefined
+
+
+ +
+
+

Convention

+

+For this presentation, the preferred way of declaring a function is a +function expression to avoid function hoisting. +

+ +
+ +
var theLongerWay = function isTheSaferWay() {
+    return true;
+};
+
+
+ +
+
+
+
+

arguments

+

+Every function has an implicit variable arguments +

+ +
+ +
var MyModule = {
+    whatIsThis: function (/* args */) {
+        // And what is
+        console.log(arguments);
+    }
+}
+
+MyModule.whatIsThis(1, 2, 3, 4);
+// out(arguments): [1, 2, 3, 4]
+
+
+ +
+
+

My arguments

+

+An array-like object containing the arguments +

+ +
+ +
var showMeTheEvidence = function (a, b, c) {
+    var args = arguments;
+
+    console.log([a, b, c]);
+    console.log(args);
+}
+
+showMeTheEvidence(1, 2, 3) // out: [1,2,3]
+showMeTheEvidence('This', 'is', 'nuts') // out: ['This', 'is', 'nuts']
+
+
+ +
+
+

Convention

+

+Whenever I depend on the arguments keyword, I put a comment on the +function header what the local variable name would be and convert it +into an array using Array.prototype.slice.call(arguments) or the +shorter [].slice.call(arguments). +

+ +
+ +
var extraRicePlease = function (/* args */) {
+    var args = [].slice.call(arguments); // This is awkward
+
+    args.push('Extra rice'); // Adding arguments
+
+    console.log(args);
+};
+
+extraRicePlease('BBQ', 'Ice Tea'); // out: ['BBQ', 'Ice Tea', 'Extra Rice']
+
+
+ +
+
+
+
+

f.call() and f.apply()

+

+Every function has the method call and apply to invoke them functionally +

+ +
+ +
var fullName = function (firstName, lastName) {
+    // return [firstName, lastName].join(' ');
+    return firstName + ' ' + lastName;
+};
+
+// Normal invokation
+console.log(fullName('Francis', 'Murillo')); // out: Francis Murillo
+
+// Using call() without the
+console.log(fullName.call(null, 'Mr', 'Robot')); // out: 'Mr Robot'
+
+// Using apply()
+console.log(fullName.apply(null, ['Jake', 'Dog'])); // out: 'Jake Dog'
+
+
+ +
+
+

Convention

+

+call and apply will always have null as the first argument +since we don't care about this. +

+ +

+apply is the more preferred way of calling functions if you don't +know the arguments, and call if you do. +

+ +
+
+
+
+

Closure / Function Scope

+

+Inner functions have access to it's the variables of the outer function +

+ +
+ +
var outerFunction = function (outerArg) {
+    var outerVar = outerArg + 10;
+
+    return function innerFunction(innerArg) {
+        var innerVar = outerVar + innerArg;
+
+        return [outerArg, outerVar, innerArg, innerVar];
+    }
+}
+
+var newFunction = outerFunction(10);
+// outerArg: 10
+// outerVar: 20
+
+console.log(newFunction(20));
+// out: [10, 20, 20, 40]
+
+console.log(newFunction(100));
+// out: [10, 20, 100, 120];
+
+
+ +
+
+
+
+

The Power Of Functions In JavaScript

+
+

+Good design is not about making grand plans, but about taking things +apart. +

+ +

+– Rich Hickey +

+
+ +

+At last, let's talk about functions. +

+ +
+
+

Functional Programming In JavaScript

+

+I don't want to bore you with a definition. It's really just a mindset +

+ +
    +
  • Input and Output
  • +
  • Combining Functions
  • +
  • Process over Step
  • +
  • Everything is a Function
  • +
  • Avoiding State
  • + +
+ +
+
+

Why I Like Functional Programming?

+
    +
  • First principles
  • +
  • Easier to write good code
  • +
  • Easier to reason
  • +
  • Natural and Mathematical
  • +
  • Fun and Creative
  • + +
+ +
+
+

What This Is Not

+
    +
  • A full course in this paradigm
  • +
  • A pure functional style
  • +
  • An introduction to JavaScript
  • +
  • Easy
  • + +
+ +
+
+

What This Is

+

+To introduce you to think and write in a functional style can help +you write good code in JavaScript and nothing more. +

+ +
+
+
+
+

The Idea

+
+

+What is the most resilient parasite? Bacteria? A virus? An intestinal +worm? An idea. Resilient… highly contagious. Once an idea has taken +hold of the brain it's almost impossible to eradicate. An idea that is +fully formed - fully understood - that sticks; right in there +somewhere. +

+ +

+– Cobb from Inception +

+
+ +
+
+

The Ideas Of Functional Programming For JavaScript

+

+The whole of the presentation +

+ +
    +
  • Thinking in terms of collections or as a whole
  • +
  • Separating behaviors and combining them
  • +
  • map, filter and reduce
  • +
  • compose and curry
  • + +
+ +
+
+

Caveat

+

+You do not have to understand the code, just feel and see that this +is much cleaner and better as a whole. +

+ +

+The idea is more important, ignore the nuances of the language but +see that it is easily done. +

+ +
+
+
+
+

Thinking With Functions

+

+Let's talk about lists and their functions namely +

+ +

+Spoilers +

+ +
    +
  • .forEach
  • +
  • .map()
  • +
  • .filter
  • +
  • .reduce
  • + +
+ +
+
+

A Question Of Intent

+

+Who here uses a for loop like so? +

+ +
+ +
var items = ['Hey Arnold', 'Adventure Time', 'JavaScript: The Good Parts'];
+
+for (var i = 0; i < items.length; i += 1) {
+    console.log(item[i]);
+}
+
+
+ +
+
+

What's Wrong With For Loops?

+

+Intention +

+ +

+When iterating through an list, one should not be concerned with the +length and index of it. All you need is the element and do something +with it. +

+ +
+ +
var heroes = [ 'Cloud', 'Tyler', 'Joel'];
+
+for (var i = 0; i < heroes.length; i+= 1) {
+    var hero = heroes[i];
+
+    if (hero === 'Tyler') { // The intent
+        console.log('Everything is free of charge.');
+    }
+}
+
+
+ +

+How do we loop over each element without being concerned with the +loop mechanics? +

+ +
+
+

No More For Loops

+

+Our journey starts by using the forEach method of an list which +allows you to loop through a list given a callback. +

+ +
+ +
var assassins = [ 'Altair', 'Ezio', 'Connor'];
+
+// Use forEach to iterate thorugh a list
+assassins.forEach(function getConnor(assassin) { // Give the function a descriptive name
+    if (assassin === 'Connor') {
+        console.log('Where is Charles Lee');
+    }
+});
+
+
+ +

+Now this looks better, we separated the intention and the +looping mechanism is now hidden as an abstraction. +

+ +

+But did you notice we passed a function to another function(the callback). +

+ +
+
+

0th Virtue: Function Variables

+

+The bare minimum that allows JavaScript to be functional is that +functions are first class objects meaning you can pass functions as +arguments and as variables. +

+ +
+ +
var myHandler = function (x) { // Assigning functions to variables
+    return 'You shall not pass';
+};
+
+var executeHandler = function (handler) { // A function taking a function
+    handler('An ignored value');
+}
+
+executeHandler(myHandler); // Passing functions around
+
+
+ +

+If JavaScript did not have this feature, we would not be talking +about it. +

+ +
+
+

Implementing forEach

+

+Let's implement our own forEach for analysis +

+ +
+ +
// Naive implentation
+var forEach = function (f, xs) {
+    for (var i = 0; i < xs.length; i+=1) {
+        var x = xs[i];
+
+        f(x);
+    }
+};
+
+var pi = [3, 1, 4, 1, 6, 1, 8];
+
+// Almost looks the same
+forEach(function displayTheNumber(n) {
+    console.log(n);
+}, xs);
+
+
+ +

+Pretty easy but this demonstrates how passing functions can improve +the logic +

+ +
+
+

Transforming Collections

+

+Let's move on a similar case of transforming a collection. +

+ +

+How about given a list of text, capitalize each one. +

+ +
+ +
var words = ['What', 'Are', 'Words']; // Base list
+
+var upperWords = []; // New list which is just a placeholder
+
+words.forEach(function capitalize(word) {
+    var upperWord = word.toUpperCase();
+
+    upperWords.push(upperWord); // Add the value to the list
+});
+
+console.log(upperWords); // out: ['WHAT', 'ARE', 'WORDS']
+
+
+ +

+In this case, we want to capitalize the whole list, not +capitalize each word in the list. +

+ +
+
+

The Intention

+

+The intention of the problem if we define it as a function. +

+ +
+ +
var capitalize = function theLoopIntention(text) {
+    return text.toUpperCase(); // Notice our function has a return
+};
+
+
+ +

+What if we can just apply it as a whole instead of defining it? +

+ +
+
+

Using map

+

+Thankfully the map method of lists allows this. +

+ +
+ +
var words = ['What', 'Are', 'Words']; // Base list
+
+var capitalize = function theLoopIntention(text) {
+    return text.toUpperCase();
+};
+
+// Just like forEach
+var newWords = words.map(capitalize);
+
+// or if you want to inline it
+var newWordsInline = words.map(function _inlineCapitalize(word) {
+    return text.toUpperCase();
+});
+
+console.log(newWords); // Like the last
+
+
+ +

+Again, we have cleanly separated the intention from the loop. +

+ +
+
+

Examples of map

+

+Just some examples to get improve comprehension +

+ +
+ +
var people = [
+    { firstName: 'Linus', lastName: 'Van Pelt', nickname: 'Sweet Baboo'},
+    { firstName: 'Charlie', lastName: 'Brown', nickname: 'Blockhead' }
+];
+
+var getNickname = function (person) {
+    return person.nickname;
+};
+
+var getFullName = function (person) {
+    return person.firstName + ' ' + person.lastName;
+};
+
+var capitalize = function theLoopIntention(text) {
+    return text.toUpperCase();
+};
+
+
+// Case: Getting a property from a list of objects
+console.log(people.map(getNickname)); // out: ['Sweet Baboo', 'Blockhead']
+
+console.log(people // You can chain maps by the way
+            .map(getFullName)
+            .map(capitalize)); // out: ['LINUS VAN PELT', 'CHARLIE BROWN']
+
+
+ +
+
+

Implementing map

+

+Just like with forEach, implenting the map is easy. +

+ +
+ +
var map = function (f, xs) {
+    var ys = [];
+
+    xs.forEach(function (x) {
+        var y = f(x);
+
+        ys.push(y);
+    });
+
+    return ys;
+};
+
+var wrapAsText = function (x) {
+    return '<p>' + x + '<p/>';
+};
+
+var labels = ['First Name', 'Last Name'];
+
+console.log(map(wrapAsText, labels));
+// out: ['<p>First Name</p>', '<p>Last Name</p>'];
+
+
+ +

+Again we separated the intention from the implementation. Isn't this +a recurring theme? +

+ +
+
+

1st Virtue: Input And Output

+

+The user of the function should only be concerned with the input and +output, not how it is done. It is transforming the inputs to produce +the output. +

+ +

+In the case of map, given a list and a transformation function, +return a transformed list. You should not care how it did it, be it a +for loop or a forEach function or a recursion. +

+ +

+What, not how is the thinking style here. +

+
+
+

Reflection: forEach and map

+

+So far we just separated the intent from our for loop and we came up +with a neat little behavior. +

+ +

+Cool, so what's next? +

+ +
+
+

Shrinking Collections

+

+Let's move on to filtering collections given a criteria. +

+ +

+Say we have a list of movie titles and we only want the James Bond +films. +

+ +
+ +
var films = {
+    { name: 'Moonraker', category: 'James Bond'},
+    { name: 'Sucker Punch' category: 'Action'},
+    { name: 'Casino Royale', category: 'James Bond'}
+};
+
+var isJamesBondFilm = function _predicateIntention(film) {
+    return film.category === 'James Bond';
+};
+
+whatFunctionIsThis(isJamesBondFilm, films);
+
+
+ +

+There is a function for lists called filter that can get our job done +

+ +
+
+

Predicate Functions

+

+Before we use filter, a bit of terminology. +

+ +

+A predicate function is just a function that returns true or +false +

+ +
+ +
// Predicate functions
+var equal = function (a, b)  { return a === b; },
+    isOdd = function (n) { return n % 2 === 0; },
+    alwaysTrue = function () { return true; }; // Always returns true
+
+// Not predicate functions
+var toString = function (x) { return x.toString(); }, // Text value
+    concat = function (xs, ys) { return xs.concat(ys); }; // List value
+
+
+ +
+
+

Using filter

+

+Given a predicate or a boolean function and a list, return a new +list where the predicate is true over the list. +

+ +
+ +
// Some trivial examples
+var numbers = [1, 2, 3, 4, 5];
+
+var isOdd = function (number) {
+    return number % 2 === 1;
+};
+
+var isTheOne = function _heyJetLi(number) {
+    return number === 1;
+};
+
+
+console.log(numbers.filter(isOdd)); // out: [1,3,5]
+console.log(numbers.filter(isTheOne)); // out: [1]
+
+
+ +

+So we have another way to work with lists. +

+ +
+
+

Implementing filter

+

+Again, we implement this for our own benefit. +

+ +
+ +
// Another naive
+var filter = function (p, xs) {
+    var ys = [];
+
+    forEach(function _checkPredicate(x) {
+        var y = x; // Just to get the name right with ys
+
+        if (p(x)) {
+            ys.push(y);
+        }
+    });
+
+    return ys;
+};
+
+var isLowerCase = function (text) {
+    return text.isLowerCase() === text;
+};
+
+var vendors = ['TORGUE', 'Hyperion', 'dahl'];
+
+console.log(filter(isLowerCase, vendors)); // out: ['dahl']
+
+
+ +
+
+

Nested Ifs

+

+Who here likes nested if statements? +

+ +
+ +
var a === true,
+    b === false;
+
+if (!a) {
+    if (b) {
+        // Two ifs and you might want to refactor this if you can
+    } else {
+    }
+}
+
+var c === false;
+
+if (!c) {
+    if (!b) {
+        if (!a) {
+            // Who the hell will write this?
+        }
+    }
+}
+
+
+ +

+I don't like branching, it's too easy to do and easy to make the code +more complicated to reason about with too many ifs. +

+ +
+
+

Code Review

+

+Consider this code as an example of nesting ifs. Given a list of +books that are to be processed and a book isbn table, find all the +unprocessed books without an isbn +

+ +
+ +
var books = [
+    { name: 'The Picture Of Dorian Gray', processed: false},
+    { name: 'Minecraft For Dummies', processed: true},
+    { name: 'Functional Programming In Javascript', processed: false}
+];
+
+var bookIsbnTable = {
+    'The Picture Of Dorian Gray': '1234',
+    'Minecraft For Dummies': '2344',
+    'Skyrim: The Official Guide': '3450'
+}
+
+var booksWithoutIsbn = [];
+
+books.forEach(function (book) {
+    if (!book.isProcessed) {
+        var isbn = get(book.name, bookIsbnTable);
+
+        if (!isbn) {
+            booksWithoutIsbn.push(book);
+        }
+    }
+})
+
+
+ +

+Let's refactor this to get to my point +

+ +
+
+

Refactor 01: Separate Intentions

+

+Separate the main intentions +

+ +
+ +
var books = [ /* ... */];
+
+var bookIsbnTable = { /* ... */ };
+
+// Intentions
+var isProcessed = function (book) {
+    return book.isProcessed === true;
+};
+
+var hasNoIsbn = function (book) {
+    var isbn = get(book.name, bookIsbnTable);
+
+    return !isbn;
+}
+
+var booksWithoutIsbn = [];
+
+books.forEach(function (book) {
+    if (isProcessed(book)) {
+        if (hasIsbn(book)) {
+            booksWithoutIsbn.push(book);
+        }
+    }
+})
+
+
+ +
+
+

Refactor 02: Use Filter

+

+Let's remove the first if statement using the new found filter power +

+ +
+ +
var books = [ /* ... */];
+
+var bookIsbnTable = { /* ... */ };
+
+// Intentions
+var isProcessed = function (book) {
+    return book.isProcessed === true;
+};
+
+var hasNoIsbn = function (book) {
+    var isbn = get(book.name, bookIsbnTable);
+
+    return !isbn;
+}
+
+var booksWithoutIsbn = [];
+
+books
+    .filter(isProcessed) // The if statement becomes a transformation
+    .forEach(function (book) {
+        if (hasNoIsbn(book)) { // Just one if left
+            booksWithoutIsbn.push(book);
+        }
+    });
+
+
+ +

+We removed one if, can we remove the other? +

+ +
+
+

Refactor 03: Use Filter Again

+

+We can chain the filter to remove the other if +

+ +
+ +
var books = [ /* ... */];
+
+var bookIsbnTable = { /* ... */ };
+
+// Intentions
+var isProcessed = function (book) {
+    return book.isProcessed === true;
+};
+
+var hasNoIsbn = function (book) {
+    var isbn = get(book.name, bookIsbnTable);
+
+    return !isbn;
+}
+
+var booksWithoutIsbn = [];
+
+books
+   .filter(isProcessed)
+   .filter(hasNoIsbn) // We not have a filter chain
+   .forEach(function (book) { // This is somewhat awkward to have
+       booksWithoutIsbn.push(book);
+   });
+
+
+ +

+And maybe we can remove the awkward forEach here +

+ +
+
+

Refactor 04: Just filter

+

+We just let the filter chain be the result and we're done +

+ +
+ +
var books = [ /* ... */];
+
+var bookIsbnTable = { /* ... */ };
+
+// Intentions
+var isProcessed = function (book) {
+    return book.isProcessed === true;
+};
+
+var hasNoIsbn = function (book) {
+    var isbn = get(book.name, bookIsbnTable);
+
+    return !isbn;
+}
+
+var booksWithoutIsbn = books
+    .filter(isProcessed)
+    .filter(hasNoIsbn);
+
+
+ +

+Although contrived, we just eliminated the if statements with the +filter function. +

+ +
+
+

Avoiding Ifs With filter

+

+Notice how the code is much more readable and easier to understand +without the explicit ifs? +

+ +
+ +
var booksWithoutIsbn = books
+    .filter(isProcessed)
+    .filter(hasNoIsbn);
+
+
+ +

+The other thing about this is that the if is a process flow or a +transformation over the list, not as a branching logic. +

+ +

+Also notice that if someone wants to add another condition, they are +more likely to add another filter to the chain and less likely to +just hack the if condition. +

+ +
+
+

Reflection: filter

+

+So we gained a new capability to filter a list and it applies to +objects as well. Again this stems from the fact that we are just +refactoring. +

+ +

+This leads us to the next virtue +

+ +
+
+

2nd Virtue: Data Abstraction

+

+By thinking about the operations over data, we can abstract the +behavior to other containers. +

+ +
+ +
var f = function (x) {
+    // Do something with x
+}
+
+mapX(f, xs); // What is xs? What if xs is a Promise, an Observable
+filterX(f, xs); // Sometimes we don't really care
+
+
+ +

+Again the process matters more than the data. If there was another +data type the same ideas can come into place. +

+ +
+
+

Collecting Collections

+

+Finally, we move on to collecting or aggregating all the values of an collection. +

+ +

+For example, given a list of numbers, return their sum +

+ +
+ +
// You're probably getting the picture or getting bored
+var numbers = [1, 2, 3, 4, 5];
+
+var add = function (a, b) {
+    return a + b;
+};
+
+var sum = 0;
+
+numbers.forEach(function (number) {
+    sum = add(sum, number);
+});
+
+console.log(sum);
+
+
+ +

+We have the reduce function for this +

+ +
+
+

Using reduce

+

+reduce takes a combining function and a list and returns the +combined values of the list. +

+ +
+ +
var numbers = [1, 2, 3, 4];
+
+var add = function _theRealIntent(a, b) {
+    return a + b
+};
+
+var sum = numbers.reduce(function _combiningFunction(acc, number) { // Explain this bro
+    // acc is the accumulated value
+    // number functions as the number in the list
+    return acc + number;
+}, 0);
+// var sum = numbers.reduce(add, 0); // Better, once you understand combining operations
+
+console.log(sum); // out: 10
+
+
+ +

+Let's implement this like the other two +

+ +
+
+

Implementing reduce

+

+Once you implement it, the idea of combining function is easy. Again +this is just the code above that is just refactored. +

+ +
+ +
var reduce = function (oper,  initialValue,  xs) {
+    var currentValue = initialValue;
+
+    forEach(function combine(x) {
+        // Combine the currentValue and the next
+        currentValue = oper(currentValue, x);
+    });
+
+    return totalValue;
+};
+
+
+ +

+Let's have the usual examples +

+ +
+
+

Example of reduce

+

+Some basic examples of reduce +

+ +
+ +
// I like Math
+var numbers = [1, 2, 3, 4];
+
+var multiply = function (x, y)  {
+    return x * y;
+};
+
+console.log(numbers.reduce(multiply, 1)); // out: 1 * 1 * 2 * 3 * 4 = 24
+console.log(numbers.reduce(multiply, 5)); // out: 5 * 1 * 2 * 3 * 4 = 120
+
+
+ +

+How about some real examples? +

+ +
+
+

A More Specific find

+

+Sometimes in a list, you want to find a specific value given a +criteria or predicate. +

+ +

+We can implement this using filter but we can also implement it +using reduce. +

+ +
+ +
var find = function (p, defaultValue, xs) {
+    return xs.reduce(function (prev, next) {
+        return p(next) === true ? next : prev;
+    }, defaultValue);
+};
+
+var findWithFilter = function (p, defaultValue, xs) {
+    var foundValues = xs.filter(p);
+
+    return foundValues.length > 0 ? foundValues[0] : defaultValue;
+};
+
+var isTheMeaningOfLife  = function (number) {
+    return number === 42;
+};
+
+console.log(find(isTheMeaningOfLife, 0, [36, 42, 48])); // out: 42
+console.log(find(isTheMeaningOfLife, 0, [37, 43, 49])); // out: 0, the default value
+
+
+ +
+
+

Reflection: reduce

+

+So we now have a tool that aggregates or combines list. +

+ +

+We now have a trio of tools and it came naturally due to a need to +separate intention. Nothing complex has been done so far which is +impressive what a simple function can do. +

+ +
+
+

All Together Now

+

+Given a list of people, find all the people that are not minors and +compute the total salary. +

+ +
+ +
var people [
+    { name: 'Mr Midnight', age: 20, salary: 50},
+    { name: 'Mr Muffin', age: 25, salary: 60},
+    { name: 'Ms Pepper', age: 17, salary: 40}
+];
+
+var isNotAMinor = function (person) {
+    return person.age >= 18;
+}
+
+var getSalary = function (person) {
+    return person.salary;
+}
+
+var add = function (x, y) {
+    return x + y
+}
+
+console.log(people // Wow
+            .filter(isNotAMinor)
+            .map(getSalary)
+            .reduce(add)); // Nice to see the three working together
+
+
+ +
+
+

Quick Review

+

+Just a quick review of everything we've done. +

+ +
    +
  • Think in terms of input and output
  • +
  • Separate the intention from the implementation
  • +
  • map, filter and reduce should be your best friends
  • +
  • Process over data
  • + +
+ +
+
+
+
+

Combining Functions

+

+Let's get to the fun stuff. +

+ +

+Spoilers +

+ +
    +
  • compose & pipe
  • +
  • curry
  • + +
+ +
+
+

3rd Virtue: Functions As Units Of Behavior

+

+Functions represents behavior or what we have been calling +intentions. +

+ +

+Primarily, a function should represent an idea and not be tangled +with other details +

+ +
+
+

A Simple Event

+

+Let's say we have two buttons with their corresponding handlers. +

+ +
+ +
var buttonHandler = function () {
+    console.log('This button does something awesome');
+}
+
+var otherButtonHandler = function () {
+    console.log('This button does something way better than the other one');
+}
+
+
+ +

+All is well +

+ +
+
+

Adding A Counter

+

+Now, what if we want to track send information to the server that a +button was clicked? Let's say we have a sendClick function that +sends data to the server. +

+ +
+ +
var sendClick = function () { /* whatever */}
+
+var buttonHandler = function () {
+    sendClick(); // Dirty
+
+    console.log('This button does something awesome');
+}
+
+var otherButtonHandler = function () {
+    sendClick(); // What the hell are you doing here?
+
+    console.log('This button does something way better than the other one');
+}
+
+
+ +

+But this implementation is dirty, copy pasting a method for each +handler and it ruins the fact the handler should be concerned with +sending logging data or what have you. +

+ +
+
+

Simple Function Over Functions

+

+What we want is to separate the handler from the logging. What we +want is to execute a function before the main one. +

+ +

+Let's call warm up with this doBefore function combiner, we will +finally use apply to invoke the functions. +

+ +
+ +
var doBefore = function (before, main) {
+    return function _executor(/* args */) { // A function that combines functions
+        var args = [].slice.call(arguments);
+
+        before.apply(null, args); // Execute the function before
+
+        return main.apply(null, args); // Execute the main function
+    }
+}
+
+
+ +

+Let's use this in our example +

+ +
+
+

Using doBefore

+

+Let's see how it makes the code much better +

+ +
+ +
var sendClick = function () { /* whatever */}
+
+var buttonHandler = doBefore(sendClick, function () { // Wrapping the handlers
+    console.log('This button does something awesome');
+}); // This functions returns the combined function
+
+var otherButtonHandler = doBefore(sendClick, function () { // Ditto
+    console.log('This button does something way better than the other one');
+});
+
+
+ +

+Notice our handlers are just the same except they are now wrapped +with the logging function. This is what it means to combine and +separate behaviors. +

+ +
+
+

Reflection: Higher Order Functions

+

+How many times have you copy pasted a block of function to several +places without consideration for it's purpose or intent? +

+ +

+If we cleanly separate the intentions of our code and combine them +properly, we can get easy to read and understand code. +

+ +

+What we just did is make an higher order function but that's just +definition. +

+ +
+
+

Learning To Compose

+

+Once you understand that you can join functions together, you can +create a pipeline using functions. +

+ +

+Let's start with an simple problem of uppercasing every text field in +an object then converting into JSON. +

+ +
+ +
var isText = function (text) {
+    return typeof text === 'string';
+}
+
+var objectToUpperCase = function (object) {
+    return mapObject(function (value) {
+        return isText(value) ? value.toUpperCase();
+    }, object);
+};
+
+var objectToJson = function (object) {
+    return JSON.parse(object);
+};
+
+var data = { name: 'Muffin', breed: 'Puspin', age: 4 };
+
+var output = objectToJson(objectToUpperCase(data)); // The end result
+
+
+ +

+Better if the two functions can be joined as one intention, objectSerialize? +

+ +
+
+

A Messy Invokation

+

+Just to demonstrate the extra parenthesis are not just dirty, but +harder to read and maintain +

+ +
+ +
// Imagine you have three weird functions
+var x = function (v) {/* ... */},
+    y = function (v) {/* ... */},
+    z = function (v) {/* ... */};
+
+var a = null;
+
+// This is ugly
+var b = x(y(z(a)));
+
+var t = function (v) {
+    return x(y(z(v)));  // Better hide the mess like a guy
+};
+
+// Better but kinda hacked
+var c = t(a);
+
+
+ +

+What we really care about is c, not x, y, or z. What we need +is something to tie the pipeline. +

+ +
+
+

Introducting compose

+

+So the function we're looking for compose, given two functions it +will call the second one with the arguments and pass it's output to +the first function and be the function of the return value. +

+ +

+Easier to explain with code. Remember we want to remove the ugly +wrapping of parenthesis in function invokation. +

+ +
+ +
var compose = function (outer, inner) {
+    return function (/* args */) {
+        var args = arguments;
+
+        var innerValue = inner.apply(null, args),
+            outerValue = outer(firstValue);
+
+        // or which looks natural // return outer(inner.apply(null, arguments));
+        return nextValue;
+    };
+},
+
+
+ +

+Let's see this in our example above +

+ +
+
+

Using compose

+

+Let's join those two functions in with compose +

+ +
+ +
var objectToUpperCase = function (object) {
+    // ...
+};
+
+var objectToJson = function (object) {
+    // ...
+};
+
+// Tying the two functions together, looks cleaner than before
+var objectSerialize = compose(objectToJson, objectToUpperCase);
+// Remember to read from right to left in order of execution
+
+var data = {
+    // ...
+};
+
+console.log(objectSerialize(data)); // Ah, looks cleaner.
+
+
+ +

+We successfully removed those wonky parenthesis but how else can +compose help us in our journey of better code? +

+ +
+
+

Examples of compose

+

+Let's get acquainted with our new friend, make it your BFF if you +can. +

+ +
+ +
// Mathematical Examples
+var square = function (n) { return n * n; },
+    increment = function (n) { return n + 1; },
+    decrement = function (n) { return n - 1; },
+    display = function (n) { console.log(n); return n; };
+
+var nextSquare = compose(square, increment),
+    incrementTwice = compose(increment, increment),
+    addZero = compose(decrement, increment),
+    displaySquare = compose(display, square);
+
+
+ +

+Composing is better when you start using it with currying. But for +now, how do we compose more than two functions? +

+ +
+
+

Improving compose

+

+So how do we compose functions all the way to the moon? +

+ +
+ +
// Three functions
+var escapeHtml = function (text) { /* ... */ },
+    wrapAsText = function (text) { /* ... */ },
+    wrapInDiv = function (text) { /* ... */ };
+
+var data = ['5 Centimeters Per Second', 'Garden Of Words'];
+
+// Plural compose, composes.
+var htmlizer = composes( // Three compositions
+    wrapInDiv,
+    wrapAsText,
+    escapeHtml); // Do note, that this seems declarative on the process
+
+// Hypothetical scenario in converting a list into Html text
+console.log(data
+            .map(htmlizer)
+            .join('\n'));
+
+
+ +

+First, we go back to our old friends +

+ +
+
+

Some Things First

+

+Let's implement a few things to improve the readability of our grand +composes +

+ +
+ +
// Returns an reversed version  of the list
+var reverse =  function (xs) {
+    var nxs = xs.slice(); // Shortcut for copying an array
+    nxs.reverse(); // Mutates the array
+
+    return nxs;
+};
+
+// Returns the first element of the list
+var first = function (xs) {
+    return get(0,  xs); // or just xs[0] if you are in a hurry
+}
+
+// Likewise the rest of the lements of the list
+var rest = function (xs) {
+    return xs.slice(1);
+};
+
+// Just a reduce with the initial value being the first value in the list
+var reduceFirst = function (oper, xs) {
+    var initialValue = first(xs),
+      otherValues = rest(xs);
+
+    return reduce(oper, initialValue, otherValues);
+};
+
+
+ +
+
+

One More Thing

+

+I prefer to read left to right instead of right to left with my +function pipeline. +

+ +
+ +
var pipe = function (first, next) {
+    return compose(next, first);
+};
+
+var splitWords = function (sentence) { return text.split(''); },
+    splitParagraphs = function (doc) { return text.split('\n'); };
+
+// Originally
+var getWords = compose(splitWords, splitParagraphs);
+
+// Really, notice the sequence is read left to right
+var getWords2 = pipe(splitParagraphs, splitWords);
+
+
+ +

+This is just compose with the arguments reversed which might be a +small thing but helps in readability, just my prefrence anyway. +

+ +
+
+

Implementing composes

+

+With that out of the way, we simply use reduceRight and pipe +in one master stroke +

+ +
+ +
var composes = function (/* fs */) {
+    var fs = [].slice.call(arguments);
+
+    // You can use compose instead of pipe but you would have to reverse the arguments
+    return reduceFirst(pipe, reverse(fs));
+};
+
+
+ +

+It is just basically starting at the end and composing all the way +back. That's how easy it is to implement composes. +

+ +
+
+

Equivalently pipes

+

+Just for the sake of symmetry. +

+ +
+ +
var pipes = function (/* fs */) {
+    var fs = [].slice.call(arguments);
+
+    return reduceFirst(pipe, fs); // This is just without the reverse
+}
+
+
+ +

+Now let's see how they fit together. +

+ +
+
+

Checking Out composes

+

+Let's check it out. +

+ +
+ +
var twice = function (n) { return n * 2; },
+    half = function (n) { return n / 2; },
+    increment = function (n) { return n + 1; };
+
+var sequence = composes(half, twice, increment);
+
+var sequence2 = pipes(increment, twice, half, twice);
+
+var sequence3 = pipes( // Preferred way of writing
+    increment,
+    twice,
+    half,
+    twice
+);
+
+
+ +

+Viola, we can compose as much as we want. But where does this leave us? +

+ +
+
+

Function Pipelines

+

+It's not the fact that you use compose or pipe, but rather that +you want your code to be a single and straight process. +

+ +

+For example, displaying data in html. The process you want is. +

+ +
    +
  • Source
  • +
  • Map
  • +
  • Render
  • + +
+ +

+But we tend to diverge from this as we add more features. The point +is you want to maintain the integrity of the process. +

+ +
+
+

Avoiding Ifs With Composition

+

+Let's have another case review. Given a list of employees and there +salary, let's compute their take home pay. +

+ +
+ +
var codeCats = [
+    { name: 'Mr. Muffin', salary: 100, gender: 'male'},
+    { name: 'Ms. Fuzbudget', salary: 50, gender: 'female'}
+];
+
+var payComputer = function (codeCat) {
+    var pay = codeCat.salary / 2;
+
+    return set('pay', pay, codeCat);
+};
+
+console.log(codeCats.map(payComputer));
+
+
+ +

+All is fuzzy and warm +

+ +
+
+

Gender Inequality

+

+Now what if every female employee gets paid 50% more due to a +goverment law. What do you do to make it right? +

+ +
+ +
var codeCats = [
+    { name: 'Mr. Muffin', salary: 100, gender: 'male'},
+    { name: 'Ms. Fuzbudget', salary: 50, gender: 'female'}
+];
+
+var payComputer = function (codeCat) {
+    var pay = codeCat.salary / 2;
+
+    var newerPay = codeCat.gender === 'female' ? pay * 1.5 : pay;
+
+    return set('pay', newerPay, codeCat);
+};
+
+console.log(codeCats.map(payComputer));
+
+
+ +

+Not a problem, but what if you can't modify payComputer because +it's a third party shiznit? Or what if there is another law, are we +going to add another if? +

+ +

+You know where this is going. +

+ +
+
+

Composition To The Rescue

+

+Let's use composition to make the code cleaner. +

+ +
+ +
var codeCats = [
+    { name: 'Mr. Muffin', salary: 100, gender: 'male'},
+    { name: 'Ms. Fuzbudget', salary: 50, gender: 'female'}
+];
+
+var femalePayRaise = function (codeCat) {
+    var basePay = codeCat.pay; // Must already be paid
+
+    return set('pay', codeCat.gender === 'female' ? basePay * 1.5 : basePay, codeCat);
+};
+
+var payComputer = compose( // Process is maintained
+    femalePayRaise,
+    function _originalComputer(codeCat) {
+        var pay = codeCat.salary / 2;
+
+        return set('pay', newerpay, codeCat);
+    });
+
+console.log(codeCats.map(payComputer)); // Still well
+
+
+ +

+We retain the original intent and managed complexity. +

+ +
+
+

Reflection: Compose Behaviors

+

+Although the example is quite contrived, the main point is to avoid +complexity and maintain the simplicity of the process. +

+ +

+The thinking style should be a composition pipeline. A straight flow +is better than a branching sewer. +

+ +

+As a tip, whenever there is a new functionality or rule, consider +composition or the like to manage the complexity. +

+ +
+
+

Reflection: composes

+

+So we just implemented one of the cornerstones of functional +programming and it wasn't complicated. All we wanted was to remove +the dirty parenthesis, amazing. +

+ +

+Let's go learn the complimentary technique for composition. +

+ +
+
+

Learning To Curry

+

+Currying is a nice functional idea to sweeten composition +

+ +

+Consider this multiplication function, what if we want the first +argument to be preset. +

+ +
+ +
var multiply = function (x, y) { return x * y; }
+
+// What if we want have the following?
+var multiplyByTwo = function (y) { return multiply(2, y); },
+    multiplyByFive = function (y) { return multiply(5, y); };
+
+// There is a sort of duplication here. How about?
+var multiplyPreset = function (x) { // Neat little trick
+    return function (y) { // Return a function where the first argument is preset
+        return multiply(x, y); // Execute the actual function with the arguments
+    };
+};
+
+// Same as above but notice we removed the noise in declaring the functions?
+var multiplyWithTwo = multiplyPreset(2),
+    multiplyWithFive = multiplyPreset(5);
+
+console.log(multiplyWithFive(4)); // out: 20
+console.log(multiplyWithTwo(2)); // out: 4
+
+
+ +
+
+

The Neat Trick

+

+Let's have another example with a function that takes three arguments +

+ +
+ +
// Consider an function that add triples
+var addTriple = function (x, y, z) { return x + y + z; };
+
+// Let's repeat the neat little trick
+var addTriplePreset = function (x) {
+    return function (y) {
+        return function (z) { // Looks kinda coo
+            return addTriple(x, y, z);
+        };
+    };
+};
+
+var addTen = addTriplePreset(10);
+
+console.log(addTen(4, 5)); // out: 19
+
+var addTenAndFive = addTriplePreset(10)(5);
+
+console.log(addTenAndFive(6)); // out: 21
+
+
+ +
+
+

Naming The Trick

+

+But this seems to be a function behavior, not logic. Maybe we can +separate this behavior. +

+ +
+ +
var curry = function (f) { // Thanks Haskell Curry
+    /* ??? */
+};
+
+// Let's curry our get function
+// get takes the key and the object
+var getId = curry(get)('id'); // or function (object) { return get('id', object); }
+
+var users = [
+    { id: 1, username: 'Beethoven'},
+    { id: 2, username: 'Mozart'},
+    { id: 3, username: 'Ravel'}
+];
+
+console.log(users.map(getId)); // out: [1,2,3];
+
+console.log(users.map(function _getId(user) { // Compare with this
+    return get('id', user);
+}));
+
+
+ +

+Let's implement the easy version +

+ +
+
+

Currying For Two

+

+Copy pasting our trick from before +

+ +
+ +
var curry = function (f) {
+    return function (x) {
+        return function (y) {
+            return f.apply(null, [x, y]); // or f(x, y);
+        }
+    }
+};
+
+
+ +

+This is enough but what if you want to implement it for three +arguments or four? You could copy paste the same definitions over +and over. +

+ +

+But there is a better way. +

+ +
+
+

Implementing curry

+

+This is the most fun and challenging to code but the main concept is +to keep returning a function until enough arguments have been +supplied and then invoke it. +

+ +
+ +
var curry = function _recursiveCurry(f) {
+    // Get the number of arguments in a function
+    var numberOfArgs = f.length;
+
+    // An helper function to curry the original function
+    var currier = function _recursiveCurry(previousArgs) {
+        return function _curried(/* currentArgs */) {
+            var currentArgs = [].slice.call(arguments),
+                newArgs = previousArgs.concat(currentArgs);
+
+            // If there is enough args, invoke the function
+            if (newArgs.length >= numberOfArgs) {
+                return f.apply(null, arguments);
+            } else { // If there is not enough args yet, keep currying
+                return _recursiveCurry(newArgs);
+            }
+        };
+    };
+
+    return currier([]); // Start recursion with no arguments
+};
+
+
+ +

+This is the only thing that looks complicated, I swear. +

+ +
+
+

Using curry

+

+Just a contrived use of the generic curry +

+ +
+ +
var computeName = curry(function _fullName(firstName, lastName) {
+    return firstName + ' ' + lastName;
+});
+
+var doctorName = computeName('Doctor');
+
+console.log(doctorName('Death')); // out: Doctor Death
+console.log(doctorName('Who')); // out: Doctor Who
+
+var johnName = computeName('John');
+
+console.log(johnName('Marshton')); // out: John Marshton
+console.log(johnName('Doe')); // out: John Doe
+
+// Cute tricks with it, but don't do this
+console.log(computeName('Linus')()('Van Pelt')); // out: Linus Van Pelt
+console.log(computeName()()()('Linus', 'Van Pelt')); // out: Linus Van Pelt
+
+
+ +

+But where does this fit in with compose? +

+ +
+
+

The Real Trick

+

+Curry a function until it has one argument left which you can pass +through a composition or mapping pipeline. +

+ +
+ +
var heroes = [
+    { name: 'Yang Xiao Long', team: 'RWBY'},
+    { name: 'Ruby Rose', team: 'RWBY' },
+    { name: 'Pyrrha Nikos', team: 'JNPR'}
+];
+
+// Remember set takes three arguments: key, value, object
+var setHealth = curry(set)('hp', 100),
+    setScore = curry(set)('score', 0);
+
+console.log(heroes
+            .map(setHealth)
+            .map(setScore));
+
+// or if you care about performance
+var setStats = compose(setHealth, setScore); // or pipe(setScore, setHealth);
+
+console.log(heroes.map(setStats));
+// Did I declare a function??
+
+
+ +

+Doesn't it look nice? We just created new functions from old ones and +given them more specific uses. +

+ +
+
+

Currying And Filtering

+

+How about using it in conjuction with filter? Given a list of +heroes, I want to filter the intelligence type heroes in the list or team. +

+ +
+ +
var heroes = [
+    { name: 'Pudge', type: 'STR', kills: 15},
+    { name: 'Puck', type: 'INT', kills: 13},
+    { name: 'Lich King', type: 'INT', kills: 9}
+    { name: 'Razor', type: 'AGI', kills: 4},
+    { name: 'Ogre Magi', type: 'STR', kills: 1}
+];
+
+var eq = curry(function _equals(a, b) {
+    return a === b;
+});
+
+var getType = curry(get)('type'),
+    isInt = eq('INT');
+
+var isIntType = pipe(getType, isInt);
+
+console.log(heroes.filter(isIntType));
+
+// compare with, I leave it to you which is better
+console.log(heroes.filter(function _isIntType(hero) {
+    return heroes.type === 'INT';
+}));
+
+
+ +

+Still cool +

+ +
+
+

Reflection: curry

+

+Another cornerstone has been implemented. We now have the ability to +create functions from old one. +

+ +

+But the real question is: are there more tricks like this? +

+
+
+

4th Virtue: Everything Should Be A Function

+

+With higher ordered concepts like composition and currying, if +everything is a function then all of them benefit from higher order +abstractions. +

+ +

+There are other higher order functions that sweeten the deal. +

+ +

+Now you know why get and set are functions so that they can be +curried and composed. +

+ +
+
+

Currying Our Old Trick

+

+Tying this back to our old previous tools +

+ +
+ +
var characters = [
+    { id: 314, name: 'Dilbert', source: 'Dilbert'},
+    { id: 319, name: 'Catbert', source: 'Dilbert'},
+    { id: 325, name: 'Sally', source: 'Peanuts'}
+];
+
+var getId = curry(get)('id'),
+    getIds = curry(map)(getId); // Currying our implementation
+
+console.log(getIds(characters)); // out: [314, 319, 325]
+
+var isFromPeanuts = curry(get)('id'),
+    fromPeanuts = curry(filter)(isFromPeanuts);
+
+console.log(fromPeanuts(characters)); // out: [{id:325,name:'Sally',source:'Peanuts'}]
+
+var getIdByPeanuts = pipe(isFromPeanuts, getIds);
+
+console.log(getIdByPeanuts(characters)); // out: [325]
+
+
+ +

+If this did not impress you, I don't know what will. +

+ +
+
+

Reflection: curry

+

+We implemented one of the nice tricks of functional programming, we +can now extract functions from old one by presetting the +arguments. +

+ +

+But the idea is that combining functions is really not that hard. How +many more ways can we play with functions? +

+ +
+
+

Quick Review

+

+So those are the fundamental tricks. +

+ +
    +
  • compose, pipe and curry are fun tools to implement and have
  • +
  • Gluing functions are easy
  • +
  • Making new functions from old
  • + +
+ +
+
+
+
+

At The End Of It All

+

+Did the code look cleaner with these concepts? +

+ +

+That is where it matters. +

+ +
+
+
+
+

JavaScript Is Functional

+

+End of the line. Wrap it up fool. I hope you found +

+ +
+
+

JavaScript Is Awesome

+

+JavaScript is really cool for me because of the following. +

+ +
    +
  • First class functions
  • +
  • Closures
  • +
  • Function scoped
  • + +
+ +

+That's really it. +

+ +

+(I could say the same thing about Python which holds a special place +in my heart.) +

+ +
+
+

Reflection: Refactoring

+

+You could have noticed that all the machinery we built can be written +by hand, a simple function and a desire to make code cleaner. +

+ +

+There are a lot of other things you can do with functions, really a +lot more I didn't mention or rather you can discover like uncharted +territory. +

+ +

+Use your imagination. +

+ +
+
+

The Power Of Functions In JavaScript

+

+If you forgot what I said, here it is. +

+ +
    +
  • Use map, filter, and reduce
  • +
  • Learn compose and curry
  • +
  • Think input and output, processes not details
  • +
  • Avoid for loops and ifs
  • +
  • Hide implementation details
  • +
  • Have fun
  • + +
+ +
+
+

What I Didn't Say

+

+Compare JavaScript with Haskell if you want to see the light +

+ +
    +
  • Partial Application
  • +
  • Closures +The chapter I removed from this presentation
  • +
  • Types
  • +
  • Monads
  • +
  • Functors
  • +
  • Patterns
  • +
  • So much
  • + +
+ +
+
+

Future Of Functional Programming

+

+I am not an prophet but… +

+ +
    +
  • Functional Reactive Programming
  • +
  • React & React Native
  • +
  • Clojure / ClojureScript
  • +
  • RxJs
  • +
  • Elm
  • +
  • Java 8: Lambdas
  • + +
+ +

+Really, it's just a good time to learn a new paradigm to widen your +perspective. +

+ +
+
+
+
+

Lastly

+

+No matter what I say or what paradigm is out there. Good code is +something you work for. There is really no silver bullet. +

+ +

+Don't be a zealot. OOP and FP have their places in design and +coding. Use your judgment which works for the correct situation. +

+ +

+But really in JavaScript, I find FP to be really easy to work with +than its Prototypical nature. +

+ +
+
+
+
+

Now GTFO

+

+Thank you for letting me waste your time. I hope you learned something +or even just the idea. +

+ +
+
+
+
+

References

+
+
+
+

Books

+ + +
+
+

Libraries

+
    +
  • lodash - a good functional library
  • +
  • jquery - not obvious but it really is a monad
  • +
  • rxjs - functional reactive programming library
  • + +
+ +
+
+

Languages

+
    +
  • haskell - the language drug to functional programming
  • +
  • clojure - the functional lisp since Scheme
  • +
  • clojurescript - the lisp for javascript
  • +
  • elm - the functional javascript
  • + +
+
+
+
+
+ + + + diff --git a/presentation-shortened.org b/presentation-shortened.org new file mode 100644 index 0000000..ae19b42 --- /dev/null +++ b/presentation-shortened.org @@ -0,0 +1,1999 @@ +#+Title: The Power Of Functions In JavaScript +#+Author: Francis Murillo +#+Email: francisavmurillo@gmail.com +#+Date: Apr. 14, 2016 + +#+OPTIONS: reveal_control:t reveal_progress:nil reveal_history:nil reveal_center:t +#+OPTIONS: reveal_slide_number:nil +#+OPTIONS: reveal_rolling_links:nil reveal_keyboard:t reveal_overview:nil num:nil + +#+OPTIONS: reveal_width:1200 reveal_height:800 +#+OPTIONS: toc:nil + +#+OPTIONS: reveal_title_slide:nil +#+OPTIONS: reveal_single_file:t + +#+REVEAL_HLEVEL:1 + +#+REVEAL_MARGIN: 0.1 +#+REVEAL_MIN_SCALE: 0.5 +#+REVEAL_MAX_SCALE: 2.5 + +#+REVEAL_THEME: blood +#+REVEAL_TRANS: linear +#+REVEAL_SPEED: fast + +#+REVEAL_HEAD_PREAMBLE: + +* The Power Of Functions In JavaScript + + By *Francis Murillo* + + + On *Apr. 14, 2016* + + + At *Manila, JavaScript #06* + + + *francisavmurillo@gmail.com* + + + *https://github.com/FrancisMurillo/the-power-of-functions-in-javascript* + + +* Foolish Assumptions + + - You know the basics of JavaScript syntax + - I am talking about JavaScript in the browser + - You and I hate presentations that waste time + +* A Little Background + + Just a mediocre software developer. + +* Before We Begin + + #+BEGIN_QUOTE + Judge a man by his questions rather than his answers + + -- Voltaire + #+END_QUOTE + +#+ATTR_REVEAL: :frag t + Who here thinks *JavaScript* (in the browser) sucks? + +** JavaScript Sucks + + Where do I begin? + + - Lousy type system + - No module system + - Global by default + - No block scope + - Monkey patching + - The rest couldn't fit on one slide + + But I am not here to bash on the language. + + How do we write good code in a (sucky but awesome) language? + +** Writing *Good Code* For JavaScript + + How do write code that is... + + - Modular + - Extensible + - Reusable + - Readable + - Poetry + +#+ATTR_REVEAL: :frag t + JavaScript already has a concept for that + +#+ATTR_REVEAL: :frag t + It's not a framework, or a library or a new concept and it's been around + since ancient times + +** Function + + The building blocks of programs + + #+BEGIN_SRC javascript + var theAllPowerful = function () { + return 'Bow before me'; + } + #+END_SRC + +* Functions In JavaScript + + #+BEGIN_QUOTE + The best thing about JavaScript is its implementation of functions. It + got almost everything right. But, as you should expect with + JavaScript, it didn’t get everything right. + + -- Douglas Crockford + #+END_QUOTE + + In order to understand their supremacy, let's review what functions look like + in JavaScript first. + +** Things You Should Know + + Just a brief review of the following. + + - Functions + - =arguments= keyword + - =call= and =apply= function + - Closure / Function Scope + +* Functions + + The bread and butter of programmers + +** Declaring Functions + + There are two primary ways to declare a function. + + #+BEGIN_SRC javascript + // Function statement + function greetLucy() { + return 'Hello Lucile'; + } + + // Function expression + var greetPatty = function _greetPatty() { + return 'Hello Sir'; + }; + #+END_SRC + +** Invoking Functions + + There's three known ways to call or invoke a function. + + #+BEGIN_SRC javascript + // Math yo + var add = function _doTheMath(x, y) { + return x + y; + }; + + // The canonical function invokation + add(1, 2); + + // The functional way of calling functions + // We'll get to this later + add.call(null, 1, 2); + + // Same thing above but the arguments is passed as an array + add.apply(null, [1, 2]); + #+END_SRC + +** Returning An Output + + =return= is the output of the function. If there is no =return=, an + implicit =undefined= is the output. + + #+BEGIN_SRC javascript + function returnSomething(x) { + return 'I call ' + x.toString(); + }; + + var noReturn = function () { + // do something + } + + console.log(returnSomething('Pikachu')); // out: 'I call Pikachu' + console.log(noReturn()); // out: undefined + #+END_SRC + +** Convention + + For this presentation, the preferred way of declaring a function is a + *function expression* to avoid function hoisting. + + #+BEGIN_SRC javascript + var theLongerWay = function isTheSaferWay() { + return true; + }; + #+END_SRC + +* =arguments= + + Every function has an implicit variable *arguments* + + #+BEGIN_SRC javascript + var MyModule = { + whatIsThis: function (/* args */) { + // And what is + console.log(arguments); + } + } + + MyModule.whatIsThis(1, 2, 3, 4); + // out(arguments): [1, 2, 3, 4] + #+END_SRC + +** My =arguments= + + An array-like object containing the arguments + + #+BEGIN_SRC javascript + var showMeTheEvidence = function (a, b, c) { + var args = arguments; + + console.log([a, b, c]); + console.log(args); + } + + showMeTheEvidence(1, 2, 3) // out: [1,2,3] + showMeTheEvidence('This', 'is', 'nuts') // out: ['This', 'is', 'nuts'] + #+END_SRC + +** Convention + + Whenever I depend on the =arguments= keyword, I put a comment on the + function header what the local variable name would be and convert it + into an array using =Array.prototype.slice.call(arguments)= or the + shorter =[].slice.call(arguments)=. + + #+BEGIN_SRC javascript + var extraRicePlease = function (/* args */) { + var args = [].slice.call(arguments); // This is awkward + + args.push('Extra rice'); // Adding arguments + + console.log(args); + }; + + extraRicePlease('BBQ', 'Ice Tea'); // out: ['BBQ', 'Ice Tea', 'Extra Rice'] + #+END_SRC + +* =f.call()= and =f.apply()= + + Every function has the method =call= and =apply= to invoke them functionally + + #+BEGIN_SRC javascript + var fullName = function (firstName, lastName) { + // return [firstName, lastName].join(' '); + return firstName + ' ' + lastName; + }; + + // Normal invokation + console.log(fullName('Francis', 'Murillo')); // out: Francis Murillo + + // Using call() without the + console.log(fullName.call(null, 'Mr', 'Robot')); // out: 'Mr Robot' + + // Using apply() + console.log(fullName.apply(null, ['Jake', 'Dog'])); // out: 'Jake Dog' + #+END_SRC + +** Convention + + =call= and =apply= will always have =null= as the first argument + since we don't care about =this=. + + =apply= is the more preferred way of calling functions if you don't + know the arguments, and =call= if you do. + +* Closure / Function Scope + + Inner functions have access to it's the variables of the outer function + + #+BEGIN_SRC javascript + var outerFunction = function (outerArg) { + var outerVar = outerArg + 10; + + return function innerFunction(innerArg) { + var innerVar = outerVar + innerArg; + + return [outerArg, outerVar, innerArg, innerVar]; + } + } + + var newFunction = outerFunction(10); + // outerArg: 10 + // outerVar: 20 + + console.log(newFunction(20)); + // out: [10, 20, 20, 40] + + console.log(newFunction(100)); + // out: [10, 20, 100, 120]; + #+END_SRC + +* The Power Of Functions In JavaScript + + #+BEGIN_QUOTE + Good design is not about making grand plans, but about taking things + apart. + + -- Rich Hickey + #+END_QUOTE + + At last, let's talk about functions. + +** Functional Programming In JavaScript + + I don't want to bore you with a definition. It's really just a mindset + + - Input and Output + - Combining Functions + - Process over Step + - Everything is a Function + - Avoiding State + +** Why I Like Functional Programming? + + - First principles + - Easier to write good code + - Easier to reason + - Natural and Mathematical + - *Fun and Creative* + +** What This Is Not + + - A full course in this paradigm + - A pure functional style + - An introduction to JavaScript + - Easy + +** What This Is + + To introduce you to think and write in a functional style can help + you write good code in JavaScript and nothing more. + +* The Idea + + #+BEGIN_QUOTE + What is the most resilient parasite? Bacteria? A virus? An intestinal + worm? An idea. Resilient... highly contagious. Once an idea has taken + hold of the brain it's almost impossible to eradicate. An idea that is + fully formed - fully understood - that sticks; right in there + somewhere. + + -- Cobb from Inception + #+END_QUOTE + +** The Ideas Of Functional Programming For JavaScript + + The whole of the presentation + + - Thinking in terms of collections or as a whole + - Separating behaviors and combining them + - =map=, =filter= and =reduce= + - =compose= and =curry= + +** Caveat + + You do not have to understand the code, just feel and see that this + is much cleaner and better as a whole. + + The idea is more important, ignore the nuances of the language but + see that it is easily done. + +* Thinking With Functions + + Let's talk about lists and their functions namely + + Spoilers + + - =.forEach= + - =.map()= + - =.filter= + - =.reduce= + +** A Question Of Intent + + Who here uses a for loop like so? + + #+BEGIN_SRC javascript + var items = ['Hey Arnold', 'Adventure Time', 'JavaScript: The Good Parts']; + + for (var i = 0; i < items.length; i += 1) { + console.log(item[i]); + } + #+END_SRC + +*** What's Wrong With For Loops? + + Intention + + When iterating through an list, one should not be concerned with the + length and index of it. All you need is the element and do something + with it. + + #+BEGIN_SRC javascript + var heroes = [ 'Cloud', 'Tyler', 'Joel']; + + for (var i = 0; i < heroes.length; i+= 1) { + var hero = heroes[i]; + + if (hero === 'Tyler') { // The intent + console.log('Everything is free of charge.'); + } + } + #+END_SRC + + How do we loop over each element without being concerned with the + loop mechanics? + +*** No More For Loops + + Our journey starts by using the [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global][forEach]] method of an list which + allows you to loop through a list given a callback. + + #+BEGIN_SRC javascript + var assassins = [ 'Altair', 'Ezio', 'Connor']; + + // Use forEach to iterate thorugh a list + assassins.forEach(function getConnor(assassin) { // Give the function a descriptive name + if (assassin === 'Connor') { + console.log('Where is Charles Lee'); + } + }); + #+END_SRC + + Now this looks better, we separated the intention and the + looping mechanism is now hidden as an abstraction. + + #+ATTR_REVEAL: :frag t + But did you notice we passed a function to another function(the callback). + +** 0th Virtue: Function Variables + + The bare minimum that allows JavaScript to be functional is that + functions are first class objects meaning you can pass functions as + arguments and as variables. + + #+BEGIN_SRC javascript + var myHandler = function (x) { // Assigning functions to variables + return 'You shall not pass'; + }; + + var executeHandler = function (handler) { // A function taking a function + handler('An ignored value'); + } + + executeHandler(myHandler); // Passing functions around + #+END_SRC + + If JavaScript did not have this feature, we would not be talking + about it. + +*** Implementing forEach + + Let's implement our own =forEach= for analysis + + #+BEGIN_SRC javascript + // Naive implentation + var forEach = function (f, xs) { + for (var i = 0; i < xs.length; i+=1) { + var x = xs[i]; + + f(x); + } + }; + + var pi = [3, 1, 4, 1, 6, 1, 8]; + + // Almost looks the same + forEach(function displayTheNumber(n) { + console.log(n); + }, xs); + #+END_SRC + + Pretty easy but this demonstrates how passing functions can improve + the logic + +** Transforming Collections + + Let's move on a similar case of transforming a collection. + + How about given a list of text, capitalize each one. + + #+BEGIN_SRC javascript + var words = ['What', 'Are', 'Words']; // Base list + + var upperWords = []; // New list which is just a placeholder + + words.forEach(function capitalize(word) { + var upperWord = word.toUpperCase(); + + upperWords.push(upperWord); // Add the value to the list + }); + + console.log(upperWords); // out: ['WHAT', 'ARE', 'WORDS'] + #+END_SRC + + In this case, we want to capitalize the whole list, not + capitalize each word in the list. + +*** The Intention + + The intention of the problem if we define it as a function. + + #+BEGIN_SRC javascript + var capitalize = function theLoopIntention(text) { + return text.toUpperCase(); // Notice our function has a return + }; + #+END_SRC + + What if we can just apply it as a whole instead of defining it? + +*** Using =map= + + Thankfully the [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map][map]] method of lists allows this. + + #+BEGIN_SRC javascript + var words = ['What', 'Are', 'Words']; // Base list + + var capitalize = function theLoopIntention(text) { + return text.toUpperCase(); + }; + + // Just like forEach + var newWords = words.map(capitalize); + + // or if you want to inline it + var newWordsInline = words.map(function _inlineCapitalize(word) { + return text.toUpperCase(); + }); + + console.log(newWords); // Like the last + #+END_SRC + + Again, we have cleanly separated the intention from the loop. + +*** Examples of map + + Just some examples to get improve comprehension + + #+BEGIN_SRC javascript + var people = [ + { firstName: 'Linus', lastName: 'Van Pelt', nickname: 'Sweet Baboo'}, + { firstName: 'Charlie', lastName: 'Brown', nickname: 'Blockhead' } + ]; + + var getNickname = function (person) { + return person.nickname; + }; + + var getFullName = function (person) { + return person.firstName + ' ' + person.lastName; + }; + + var capitalize = function theLoopIntention(text) { + return text.toUpperCase(); + }; + + + // Case: Getting a property from a list of objects + console.log(people.map(getNickname)); // out: ['Sweet Baboo', 'Blockhead'] + + console.log(people // You can chain maps by the way + .map(getFullName) + .map(capitalize)); // out: ['LINUS VAN PELT', 'CHARLIE BROWN'] + #+END_SRC + +*** Implementing map + + Just like with =forEach=, implenting the =map= is easy. + + #+BEGIN_SRC javascript + var map = function (f, xs) { + var ys = []; + + xs.forEach(function (x) { + var y = f(x); + + ys.push(y); + }); + + return ys; + }; + + var wrapAsText = function (x) { + return '

' + x + '

'; + }; + + var labels = ['First Name', 'Last Name']; + + console.log(map(wrapAsText, labels)); + // out: ['

First Name

', '

Last Name

']; + #+END_SRC + + Again we separated the intention from the implementation. Isn't this + a recurring theme? + +** 1st Virtue: Input And Output + + The user of the function should only be concerned with the input and + output, not how it is done. It is transforming the inputs to produce + the output. + + In the case of =map=, given a list and a transformation function, + return a transformed list. You should not care how it did it, be it a + for loop or a forEach function or a recursion. + + *What*, not *how* is the thinking style here. +** Reflection: =forEach= and =map= + + So far we just separated the intent from our for loop and we came up + with a neat little behavior. + + Cool, so what's next? + +** Shrinking Collections + + Let's move on to filtering collections given a criteria. + + Say we have a list of movie titles and we only want the James Bond + films. + + #+BEGIN_SRC javascript + var films = { + { name: 'Moonraker', category: 'James Bond'}, + { name: 'Sucker Punch' category: 'Action'}, + { name: 'Casino Royale', category: 'James Bond'} + }; + + var isJamesBondFilm = function _predicateIntention(film) { + return film.category === 'James Bond'; + }; + + whatFunctionIsThis(isJamesBondFilm, films); + #+END_SRC + + There is a function for lists called [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter][filter]] that can get our job done + +*** Predicate Functions + + Before we use =filter=, a bit of terminology. + + A *predicate function* is just a function that returns =true= or + =false= + + #+BEGIN_SRC javascript + // Predicate functions + var equal = function (a, b) { return a === b; }, + isOdd = function (n) { return n % 2 === 0; }, + alwaysTrue = function () { return true; }; // Always returns true + + // Not predicate functions + var toString = function (x) { return x.toString(); }, // Text value + concat = function (xs, ys) { return xs.concat(ys); }; // List value + #+END_SRC + +*** Using =filter= + + Given a predicate or a boolean function and a list, return a new + list where the predicate is true over the list. + + #+BEGIN_SRC javascript + // Some trivial examples + var numbers = [1, 2, 3, 4, 5]; + + var isOdd = function (number) { + return number % 2 === 1; + }; + + var isTheOne = function _heyJetLi(number) { + return number === 1; + }; + + + console.log(numbers.filter(isOdd)); // out: [1,3,5] + console.log(numbers.filter(isTheOne)); // out: [1] + #+END_SRC + + So we have another way to work with lists. + +*** Implementing =filter= + + Again, we implement this for our own benefit. + + #+BEGIN_SRC javascript + // Another naive + var filter = function (p, xs) { + var ys = []; + + forEach(function _checkPredicate(x) { + var y = x; // Just to get the name right with ys + + if (p(x)) { + ys.push(y); + } + }); + + return ys; + }; + + var isLowerCase = function (text) { + return text.isLowerCase() === text; + }; + + var vendors = ['TORGUE', 'Hyperion', 'dahl']; + + console.log(filter(isLowerCase, vendors)); // out: ['dahl'] + #+END_SRC + +** Nested Ifs + + Who here likes nested =if= statements? + + #+BEGIN_SRC javascript + var a === true, + b === false; + + if (!a) { + if (b) { + // Two ifs and you might want to refactor this if you can + } else { + } + } + + var c === false; + + if (!c) { + if (!b) { + if (!a) { + // Who the hell will write this? + } + } + } + #+END_SRC + + I don't like branching, it's too easy to do and easy to make the code + more complicated to reason about with too many ifs. + +** Code Review + + Consider this code as an example of nesting ifs. Given a list of + books that are to be processed and a book isbn table, find all the + unprocessed books without an isbn + + #+BEGIN_SRC javascript + var books = [ + { name: 'The Picture Of Dorian Gray', processed: false}, + { name: 'Minecraft For Dummies', processed: true}, + { name: 'Functional Programming In Javascript', processed: false} + ]; + + var bookIsbnTable = { + 'The Picture Of Dorian Gray': '1234', + 'Minecraft For Dummies': '2344', + 'Skyrim: The Official Guide': '3450' + } + + var booksWithoutIsbn = []; + + books.forEach(function (book) { + if (!book.isProcessed) { + var isbn = get(book.name, bookIsbnTable); + + if (!isbn) { + booksWithoutIsbn.push(book); + } + } + }) + #+END_SRC + + Let's refactor this to get to my point + +*** Refactor 01: Separate Intentions + + Separate the main intentions + + #+BEGIN_SRC javascript + var books = [ /* ... */]; + + var bookIsbnTable = { /* ... */ }; + + // Intentions + var isProcessed = function (book) { + return book.isProcessed === true; + }; + + var hasNoIsbn = function (book) { + var isbn = get(book.name, bookIsbnTable); + + return !isbn; + } + + var booksWithoutIsbn = []; + + books.forEach(function (book) { + if (isProcessed(book)) { + if (hasIsbn(book)) { + booksWithoutIsbn.push(book); + } + } + }) + #+END_SRC + +*** Refactor 02: Use Filter + + Let's remove the first =if= statement using the new found =filter= power + + #+BEGIN_SRC javascript + var books = [ /* ... */]; + + var bookIsbnTable = { /* ... */ }; + + // Intentions + var isProcessed = function (book) { + return book.isProcessed === true; + }; + + var hasNoIsbn = function (book) { + var isbn = get(book.name, bookIsbnTable); + + return !isbn; + } + + var booksWithoutIsbn = []; + + books + .filter(isProcessed) // The if statement becomes a transformation + .forEach(function (book) { + if (hasNoIsbn(book)) { // Just one if left + booksWithoutIsbn.push(book); + } + }); + #+END_SRC + + We removed one if, can we remove the other? + +*** Refactor 03: Use Filter Again + + We can chain the =filter= to remove the other if + + #+BEGIN_SRC javascript + var books = [ /* ... */]; + + var bookIsbnTable = { /* ... */ }; + + // Intentions + var isProcessed = function (book) { + return book.isProcessed === true; + }; + + var hasNoIsbn = function (book) { + var isbn = get(book.name, bookIsbnTable); + + return !isbn; + } + + var booksWithoutIsbn = []; + + books + .filter(isProcessed) + .filter(hasNoIsbn) // We not have a filter chain + .forEach(function (book) { // This is somewhat awkward to have + booksWithoutIsbn.push(book); + }); + #+END_SRC + + And maybe we can remove the awkward =forEach= here + +*** Refactor 04: Just =filter= + + We just let the =filter= chain be the result and we're done + + #+BEGIN_SRC javascript + var books = [ /* ... */]; + + var bookIsbnTable = { /* ... */ }; + + // Intentions + var isProcessed = function (book) { + return book.isProcessed === true; + }; + + var hasNoIsbn = function (book) { + var isbn = get(book.name, bookIsbnTable); + + return !isbn; + } + + var booksWithoutIsbn = books + .filter(isProcessed) + .filter(hasNoIsbn); + #+END_SRC + + Although contrived, we just eliminated the if statements with the + =filter= function. + +*** Avoiding Ifs With =filter= + + Notice how the code is much more readable and easier to understand + without the explicit ifs? + + #+BEGIN_SRC javascript + var booksWithoutIsbn = books + .filter(isProcessed) + .filter(hasNoIsbn); + #+END_SRC + + The other thing about this is that the =if= is a process flow or a + transformation over the list, not as a branching logic. + + Also notice that if someone wants to add another condition, they are + more likely to add another filter to the chain and less likely to + just hack the if condition. + +** Reflection: =filter= + + So we gained a new capability to filter a list and it applies to + objects as well. Again this stems from the fact that we are just + refactoring. + + This leads us to the next virtue + +** 2nd Virtue: Data Abstraction + + By thinking about the operations over data, we can abstract the + behavior to other containers. + + #+BEGIN_SRC javascript + var f = function (x) { + // Do something with x + } + + mapX(f, xs); // What is xs? What if xs is a Promise, an Observable + filterX(f, xs); // Sometimes we don't really care + #+END_SRC + + Again the process matters more than the data. If there was another + data type the same ideas can come into place. + +** Collecting Collections + + Finally, we move on to collecting or aggregating all the values of an collection. + + For example, given a list of numbers, return their sum + + #+BEGIN_SRC javascript + // You're probably getting the picture or getting bored + var numbers = [1, 2, 3, 4, 5]; + + var add = function (a, b) { + return a + b; + }; + + var sum = 0; + + numbers.forEach(function (number) { + sum = add(sum, number); + }); + + console.log(sum); + #+END_SRC + + We have the [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FArray%2FReduc][reduce]] function for this + +*** Using =reduce= + + =reduce= takes a combining function and a list and returns the + combined values of the list. + + #+BEGIN_SRC javascript + var numbers = [1, 2, 3, 4]; + + var add = function _theRealIntent(a, b) { + return a + b + }; + + var sum = numbers.reduce(function _combiningFunction(acc, number) { // Explain this bro + // acc is the accumulated value + // number functions as the number in the list + return acc + number; + }, 0); + // var sum = numbers.reduce(add, 0); // Better, once you understand combining operations + + console.log(sum); // out: 10 + #+END_SRC + + Let's implement this like the other two + +*** Implementing =reduce= + + Once you implement it, the idea of combining function is easy. Again + this is just the code above that is just refactored. + + #+BEGIN_SRC javascript + var reduce = function (oper, initialValue, xs) { + var currentValue = initialValue; + + forEach(function combine(x) { + // Combine the currentValue and the next + currentValue = oper(currentValue, x); + }); + + return totalValue; + }; + #+END_SRC + + Let's have the usual examples + +*** Example of =reduce= + + Some basic examples of =reduce= + + #+BEGIN_SRC javascript + // I like Math + var numbers = [1, 2, 3, 4]; + + var multiply = function (x, y) { + return x * y; + }; + + console.log(numbers.reduce(multiply, 1)); // out: 1 * 1 * 2 * 3 * 4 = 24 + console.log(numbers.reduce(multiply, 5)); // out: 5 * 1 * 2 * 3 * 4 = 120 + #+END_SRC + + How about some real examples? + +*** A More Specific =find= + + Sometimes in a list, you want to find a specific value given a + criteria or predicate. + + We can implement this using =filter= but we can also implement it + using =reduce=. + + #+BEGIN_SRC javascript + var find = function (p, defaultValue, xs) { + return xs.reduce(function (prev, next) { + return p(next) === true ? next : prev; + }, defaultValue); + }; + + var findWithFilter = function (p, defaultValue, xs) { + var foundValues = xs.filter(p); + + return foundValues.length > 0 ? foundValues[0] : defaultValue; + }; + + var isTheMeaningOfLife = function (number) { + return number === 42; + }; + + console.log(find(isTheMeaningOfLife, 0, [36, 42, 48])); // out: 42 + console.log(find(isTheMeaningOfLife, 0, [37, 43, 49])); // out: 0, the default value + #+END_SRC + +** Reflection: =reduce= + + So we now have a tool that aggregates or combines list. + + We now have a trio of tools and it came naturally due to a need to + separate intention. Nothing complex has been done so far which is + impressive what a simple function can do. + +** All Together Now + + Given a list of people, find all the people that are not minors and + compute the total salary. + + #+BEGIN_SRC javascript + var people [ + { name: 'Mr Midnight', age: 20, salary: 50}, + { name: 'Mr Muffin', age: 25, salary: 60}, + { name: 'Ms Pepper', age: 17, salary: 40} + ]; + + var isNotAMinor = function (person) { + return person.age >= 18; + } + + var getSalary = function (person) { + return person.salary; + } + + var add = function (x, y) { + return x + y + } + + console.log(people // Wow + .filter(isNotAMinor) + .map(getSalary) + .reduce(add)); // Nice to see the three working together + #+END_SRC + +** Quick Review + + Just a quick review of everything we've done. + + - Think in terms of input and output + - Separate the intention from the implementation + - =map=, =filter= and =reduce= should be your best friends + - Process over data + +* Combining Functions + + Let's get to the fun stuff. + + Spoilers + + - =compose= & =pipe= + - =curry= + +** 3rd Virtue: Functions As Units Of Behavior + + Functions represents behavior or what we have been calling + intentions. + + Primarily, a function should represent an idea and not be tangled + with other details + +*** A Simple Event + + Let's say we have two buttons with their corresponding handlers. + + #+BEGIN_SRC javascript + var buttonHandler = function () { + console.log('This button does something awesome'); + } + + var otherButtonHandler = function () { + console.log('This button does something way better than the other one'); + } + #+END_SRC + + All is well + +*** Adding A Counter + + Now, what if we want to track send information to the server that a + button was clicked? Let's say we have a =sendClick= function that + sends data to the server. + + #+BEGIN_SRC javascript + var sendClick = function () { /* whatever */} + + var buttonHandler = function () { + sendClick(); // Dirty + + console.log('This button does something awesome'); + } + + var otherButtonHandler = function () { + sendClick(); // What the hell are you doing here? + + console.log('This button does something way better than the other one'); + } + #+END_SRC + + But this implementation is dirty, copy pasting a method for each + handler and it ruins the fact the handler should be concerned with + sending logging data or what have you. + +*** Simple Function Over Functions + + What we want is to separate the handler from the logging. What we + want is to execute a function before the main one. + + Let's call warm up with this =doBefore= function combiner, we will + finally use =apply= to invoke the functions. + + #+BEGIN_SRC javascript + var doBefore = function (before, main) { + return function _executor(/* args */) { // A function that combines functions + var args = [].slice.call(arguments); + + before.apply(null, args); // Execute the function before + + return main.apply(null, args); // Execute the main function + } + } + #+END_SRC + + Let's use this in our example + +*** Using =doBefore= + + Let's see how it makes the code much better + + #+BEGIN_SRC javascript + var sendClick = function () { /* whatever */} + + var buttonHandler = doBefore(sendClick, function () { // Wrapping the handlers + console.log('This button does something awesome'); + }); // This functions returns the combined function + + var otherButtonHandler = doBefore(sendClick, function () { // Ditto + console.log('This button does something way better than the other one'); + }); + #+END_SRC + + Notice our handlers are just the same except they are now wrapped + with the logging function. This is what it means to combine and + separate behaviors. + +** Reflection: Higher Order Functions + + How many times have you copy pasted a block of function to several + places without consideration for it's purpose or intent? + + If we cleanly separate the intentions of our code and combine them + properly, we can get easy to read and understand code. + + What we just did is make an *higher order function* but that's just + definition. + +** Learning To Compose + + Once you understand that you can join functions together, you can + create a pipeline using functions. + + Let's start with an simple problem of uppercasing every text field in + an object then converting into JSON. + + #+BEGIN_SRC javascript + var isText = function (text) { + return typeof text === 'string'; + } + + var objectToUpperCase = function (object) { + return mapObject(function (value) { + return isText(value) ? value.toUpperCase(); + }, object); + }; + + var objectToJson = function (object) { + return JSON.parse(object); + }; + + var data = { name: 'Muffin', breed: 'Puspin', age: 4 }; + + var output = objectToJson(objectToUpperCase(data)); // The end result + #+END_SRC + + Better if the two functions can be joined as one intention, =objectSerialize=? + +*** A Messy Invokation + + Just to demonstrate the extra parenthesis are not just dirty, but + harder to read and maintain + + #+BEGIN_SRC javascript + // Imagine you have three weird functions + var x = function (v) {/* ... */}, + y = function (v) {/* ... */}, + z = function (v) {/* ... */}; + + var a = null; + + // This is ugly + var b = x(y(z(a))); + + var t = function (v) { + return x(y(z(v))); // Better hide the mess like a guy + }; + + // Better but kinda hacked + var c = t(a); + #+END_SRC + + What we really care about is =c=, not =x=, =y=, or =z=. What we need + is something to tie the pipeline. + +*** Introducting =compose= + + So the function we're looking for =compose=, given two functions it + will call the second one with the arguments and pass it's output to + the first function and be the function of the return value. + + Easier to explain with code. Remember we want to remove the ugly + wrapping of parenthesis in function invokation. + + #+BEGIN_SRC javascript + var compose = function (outer, inner) { + return function (/* args */) { + var args = arguments; + + var innerValue = inner.apply(null, args), + outerValue = outer(firstValue); + + // or which looks natural // return outer(inner.apply(null, arguments)); + return nextValue; + }; + }, + #+END_SRC + + Let's see this in our example above + +*** Using =compose= + + Let's join those two functions in with =compose= + + #+BEGIN_SRC javascript + var objectToUpperCase = function (object) { + // ... + }; + + var objectToJson = function (object) { + // ... + }; + + // Tying the two functions together, looks cleaner than before + var objectSerialize = compose(objectToJson, objectToUpperCase); + // Remember to read from right to left in order of execution + + var data = { + // ... + }; + + console.log(objectSerialize(data)); // Ah, looks cleaner. + #+END_SRC + + We successfully removed those wonky parenthesis but how else can + compose help us in our journey of better code? + +*** Examples of =compose= + + Let's get acquainted with our new friend, make it your BFF if you + can. + + #+BEGIN_SRC javascript + // Mathematical Examples + var square = function (n) { return n * n; }, + increment = function (n) { return n + 1; }, + decrement = function (n) { return n - 1; }, + display = function (n) { console.log(n); return n; }; + + var nextSquare = compose(square, increment), + incrementTwice = compose(increment, increment), + addZero = compose(decrement, increment), + displaySquare = compose(display, square); + #+END_SRC + + Composing is better when you start using it with currying. But for + now, how do we compose more than two functions? + +** Improving =compose= + + So how do we compose functions all the way to the moon? + + #+BEGIN_SRC javascript + // Three functions + var escapeHtml = function (text) { /* ... */ }, + wrapAsText = function (text) { /* ... */ }, + wrapInDiv = function (text) { /* ... */ }; + + var data = ['5 Centimeters Per Second', 'Garden Of Words']; + + // Plural compose, composes. + var htmlizer = composes( // Three compositions + wrapInDiv, + wrapAsText, + escapeHtml); // Do note, that this seems declarative on the process + + // Hypothetical scenario in converting a list into Html text + console.log(data + .map(htmlizer) + .join('\n')); + #+END_SRC + + First, we go back to our old friends + +*** Some Things First + + Let's implement a few things to improve the readability of our grand + =composes= + + #+BEGIN_SRC javascript + // Returns an reversed version of the list + var reverse = function (xs) { + var nxs = xs.slice(); // Shortcut for copying an array + nxs.reverse(); // Mutates the array + + return nxs; + }; + + // Returns the first element of the list + var first = function (xs) { + return get(0, xs); // or just xs[0] if you are in a hurry + } + + // Likewise the rest of the lements of the list + var rest = function (xs) { + return xs.slice(1); + }; + + // Just a reduce with the initial value being the first value in the list + var reduceFirst = function (oper, xs) { + var initialValue = first(xs), + otherValues = rest(xs); + + return reduce(oper, initialValue, otherValues); + }; + #+END_SRC + +*** One More Thing + + I prefer to read left to right instead of right to left with my + function pipeline. + + #+BEGIN_SRC javascript + var pipe = function (first, next) { + return compose(next, first); + }; + + var splitWords = function (sentence) { return text.split(''); }, + splitParagraphs = function (doc) { return text.split('\n'); }; + + // Originally + var getWords = compose(splitWords, splitParagraphs); + + // Really, notice the sequence is read left to right + var getWords2 = pipe(splitParagraphs, splitWords); + #+END_SRC + + This is just =compose= with the arguments reversed which might be a + small thing but helps in readability, just my prefrence anyway. + +*** Implementing =composes= + + With that out of the way, we simply use =reduceRight= and =pipe= + in one master stroke + + #+BEGIN_SRC javascript + var composes = function (/* fs */) { + var fs = [].slice.call(arguments); + + // You can use compose instead of pipe but you would have to reverse the arguments + return reduceFirst(pipe, reverse(fs)); + }; + #+END_SRC + + It is just basically starting at the end and composing all the way + back. That's how easy it is to implement =composes=. + +*** Equivalently =pipes= + + Just for the sake of symmetry. + + #+BEGIN_SRC javascript + var pipes = function (/* fs */) { + var fs = [].slice.call(arguments); + + return reduceFirst(pipe, fs); // This is just without the reverse + } + #+END_SRC + + Now let's see how they fit together. + +*** Checking Out =composes= + + Let's check it out. + + #+BEGIN_SRC javascript + var twice = function (n) { return n * 2; }, + half = function (n) { return n / 2; }, + increment = function (n) { return n + 1; }; + + var sequence = composes(half, twice, increment); + + var sequence2 = pipes(increment, twice, half, twice); + + var sequence3 = pipes( // Preferred way of writing + increment, + twice, + half, + twice + ); + #+END_SRC + + Viola, we can compose as much as we want. But where does this leave us? + +** Function Pipelines + + It's not the fact that you use =compose= or =pipe=, but rather that + you want your code to be a single and straight process. + + For example, displaying data in html. The process you want is. + + - Source + - Map + - Render + + But we tend to diverge from this as we add more features. The point + is you want to maintain the integrity of the process. + +** Avoiding Ifs With Composition + + Let's have another case review. Given a list of employees and there + salary, let's compute their take home pay. + + #+BEGIN_SRC javascript + var codeCats = [ + { name: 'Mr. Muffin', salary: 100, gender: 'male'}, + { name: 'Ms. Fuzbudget', salary: 50, gender: 'female'} + ]; + + var payComputer = function (codeCat) { + var pay = codeCat.salary / 2; + + return set('pay', pay, codeCat); + }; + + console.log(codeCats.map(payComputer)); + #+END_SRC + + All is fuzzy and warm + +*** Gender Inequality + + Now what if every female employee gets paid 50% more due to a + goverment law. What do you do to make it right? + + #+BEGIN_SRC javascript + var codeCats = [ + { name: 'Mr. Muffin', salary: 100, gender: 'male'}, + { name: 'Ms. Fuzbudget', salary: 50, gender: 'female'} + ]; + + var payComputer = function (codeCat) { + var pay = codeCat.salary / 2; + + var newerPay = codeCat.gender === 'female' ? pay * 1.5 : pay; + + return set('pay', newerPay, codeCat); + }; + + console.log(codeCats.map(payComputer)); + #+END_SRC + + Not a problem, but what if you can't modify =payComputer= because + it's a third party shiznit? Or what if there is another law, are we + going to add another if? + + You know where this is going. + +*** Composition To The Rescue + + Let's use composition to make the code cleaner. + + #+BEGIN_SRC javascript + var codeCats = [ + { name: 'Mr. Muffin', salary: 100, gender: 'male'}, + { name: 'Ms. Fuzbudget', salary: 50, gender: 'female'} + ]; + + var femalePayRaise = function (codeCat) { + var basePay = codeCat.pay; // Must already be paid + + return set('pay', codeCat.gender === 'female' ? basePay * 1.5 : basePay, codeCat); + }; + + var payComputer = compose( // Process is maintained + femalePayRaise, + function _originalComputer(codeCat) { + var pay = codeCat.salary / 2; + + return set('pay', newerpay, codeCat); + }); + + console.log(codeCats.map(payComputer)); // Still well + + #+END_SRC + + We retain the original intent and managed complexity. + +*** Reflection: Compose Behaviors + + Although the example is quite contrived, the main point is to avoid + complexity and maintain the simplicity of the process. + + The thinking style should be a composition pipeline. A straight flow + is better than a branching sewer. + + As a tip, whenever there is a new functionality or rule, consider + composition or the like to manage the complexity. + +** Reflection: =composes= + + So we just implemented one of the cornerstones of functional + programming and it wasn't complicated. All we wanted was to remove + the dirty parenthesis, amazing. + + Let's go learn the complimentary technique for composition. + +** Learning To Curry + + *Currying* is a nice functional idea to sweeten composition + + Consider this multiplication function, what if we want the first + argument to be preset. + + #+BEGIN_SRC javascript + var multiply = function (x, y) { return x * y; } + + // What if we want have the following? + var multiplyByTwo = function (y) { return multiply(2, y); }, + multiplyByFive = function (y) { return multiply(5, y); }; + + // There is a sort of duplication here. How about? + var multiplyPreset = function (x) { // Neat little trick + return function (y) { // Return a function where the first argument is preset + return multiply(x, y); // Execute the actual function with the arguments + }; + }; + + // Same as above but notice we removed the noise in declaring the functions? + var multiplyWithTwo = multiplyPreset(2), + multiplyWithFive = multiplyPreset(5); + + console.log(multiplyWithFive(4)); // out: 20 + console.log(multiplyWithTwo(2)); // out: 4 + #+END_SRC + +*** The Neat Trick + + Let's have another example with a function that takes three arguments + + #+BEGIN_SRC javascript + // Consider an function that add triples + var addTriple = function (x, y, z) { return x + y + z; }; + + // Let's repeat the neat little trick + var addTriplePreset = function (x) { + return function (y) { + return function (z) { // Looks kinda coo + return addTriple(x, y, z); + }; + }; + }; + + var addTen = addTriplePreset(10); + + console.log(addTen(4, 5)); // out: 19 + + var addTenAndFive = addTriplePreset(10)(5); + + console.log(addTenAndFive(6)); // out: 21 + #+END_SRC + +*** Naming The Trick + + But this seems to be a function behavior, not logic. Maybe we can + separate this behavior. + + #+BEGIN_SRC javascript + var curry = function (f) { // Thanks Haskell Curry + /* ??? */ + }; + + // Let's curry our get function + // get takes the key and the object + var getId = curry(get)('id'); // or function (object) { return get('id', object); } + + var users = [ + { id: 1, username: 'Beethoven'}, + { id: 2, username: 'Mozart'}, + { id: 3, username: 'Ravel'} + ]; + + console.log(users.map(getId)); // out: [1,2,3]; + + console.log(users.map(function _getId(user) { // Compare with this + return get('id', user); + })); + #+END_SRC + + Let's implement the easy version + +*** Currying For Two + + Copy pasting our trick from before + + #+BEGIN_SRC javascript + var curry = function (f) { + return function (x) { + return function (y) { + return f.apply(null, [x, y]); // or f(x, y); + } + } + }; + #+END_SRC + + This is enough but what if you want to implement it for three + arguments or four? You could copy paste the same definitions over + and over. + + But there is a better way. + +*** Implementing =curry= + + This is the most fun and challenging to code but the main concept is + to keep returning a function until enough arguments have been + supplied and then invoke it. + + #+BEGIN_SRC javascript + var curry = function _recursiveCurry(f) { + // Get the number of arguments in a function + var numberOfArgs = f.length; + + // An helper function to curry the original function + var currier = function _recursiveCurry(previousArgs) { + return function _curried(/* currentArgs */) { + var currentArgs = [].slice.call(arguments), + newArgs = previousArgs.concat(currentArgs); + + // If there is enough args, invoke the function + if (newArgs.length >= numberOfArgs) { + return f.apply(null, arguments); + } else { // If there is not enough args yet, keep currying + return _recursiveCurry(newArgs); + } + }; + }; + + return currier([]); // Start recursion with no arguments + }; + #+END_SRC + + This is the only thing that looks complicated, I swear. + +*** Using =curry= + + Just a contrived use of the generic =curry= + + #+BEGIN_SRC javascript + var computeName = curry(function _fullName(firstName, lastName) { + return firstName + ' ' + lastName; + }); + + var doctorName = computeName('Doctor'); + + console.log(doctorName('Death')); // out: Doctor Death + console.log(doctorName('Who')); // out: Doctor Who + + var johnName = computeName('John'); + + console.log(johnName('Marshton')); // out: John Marshton + console.log(johnName('Doe')); // out: John Doe + + // Cute tricks with it, but don't do this + console.log(computeName('Linus')()('Van Pelt')); // out: Linus Van Pelt + console.log(computeName()()()('Linus', 'Van Pelt')); // out: Linus Van Pelt + #+END_SRC + + But where does this fit in with =compose=? + +** The Real Trick + + Curry a function until it has one argument left which you can pass + through a composition or mapping pipeline. + + #+BEGIN_SRC javascript + var heroes = [ + { name: 'Yang Xiao Long', team: 'RWBY'}, + { name: 'Ruby Rose', team: 'RWBY' }, + { name: 'Pyrrha Nikos', team: 'JNPR'} + ]; + + // Remember set takes three arguments: key, value, object + var setHealth = curry(set)('hp', 100), + setScore = curry(set)('score', 0); + + console.log(heroes + .map(setHealth) + .map(setScore)); + + // or if you care about performance + var setStats = compose(setHealth, setScore); // or pipe(setScore, setHealth); + + console.log(heroes.map(setStats)); + // Did I declare a function?? + #+END_SRC + + Doesn't it look nice? We just created new functions from old ones and + given them more specific uses. + +*** Currying And Filtering + + How about using it in conjuction with filter? Given a list of + heroes, I want to filter the intelligence type heroes in the list or team. + + #+BEGIN_SRC javascript + var heroes = [ + { name: 'Pudge', type: 'STR', kills: 15}, + { name: 'Puck', type: 'INT', kills: 13}, + { name: 'Lich King', type: 'INT', kills: 9} + { name: 'Razor', type: 'AGI', kills: 4}, + { name: 'Ogre Magi', type: 'STR', kills: 1} + ]; + + var eq = curry(function _equals(a, b) { + return a === b; + }); + + var getType = curry(get)('type'), + isInt = eq('INT'); + + var isIntType = pipe(getType, isInt); + + console.log(heroes.filter(isIntType)); + + // compare with, I leave it to you which is better + console.log(heroes.filter(function _isIntType(hero) { + return heroes.type === 'INT'; + })); + #+END_SRC + + Still cool + +** Reflection: =curry= + + Another cornerstone has been implemented. We now have the ability to + create functions from old one. + + But the real question is: are there more tricks like this? +** 4th Virtue: Everything Should Be A Function + + With higher ordered concepts like composition and currying, if + everything is a function then all of them benefit from higher order + abstractions. + + There are other higher order functions that sweeten the deal. + + Now you know why =get= and =set= are functions so that they can be + curried and composed. + +** Currying Our Old Trick + + Tying this back to our old previous tools + + #+BEGIN_SRC javascript + var characters = [ + { id: 314, name: 'Dilbert', source: 'Dilbert'}, + { id: 319, name: 'Catbert', source: 'Dilbert'}, + { id: 325, name: 'Sally', source: 'Peanuts'} + ]; + + var getId = curry(get)('id'), + getIds = curry(map)(getId); // Currying our implementation + + console.log(getIds(characters)); // out: [314, 319, 325] + + var isFromPeanuts = curry(get)('id'), + fromPeanuts = curry(filter)(isFromPeanuts); + + console.log(fromPeanuts(characters)); // out: [{id:325,name:'Sally',source:'Peanuts'}] + + var getIdByPeanuts = pipe(isFromPeanuts, getIds); + + console.log(getIdByPeanuts(characters)); // out: [325] + #+END_SRC + + If this did not impress you, I don't know what will. + +** Reflection: =curry= + + We implemented one of the nice tricks of functional programming, we + can now extract functions from old one by presetting the + arguments. + + But the idea is that combining functions is really not that hard. How + many more ways can we play with functions? + +** Quick Review + + So those are the fundamental tricks. + + - =compose=, =pipe= and =curry= are fun tools to implement and have + - Gluing functions are easy + - Making new functions from old + +* At The End Of It All + + Did the code look cleaner with these concepts? + + That is where it matters. + +* JavaScript Is Functional + + End of the line. Wrap it up fool. I hope you found + +** JavaScript Is Awesome + + JavaScript is really cool for me because of the following. + + - First class functions + - Closures + - Function scoped + + That's really it. + + (I could say the same thing about *Python* which holds a special place + in my heart.) + +** Reflection: Refactoring + + You could have noticed that all the machinery we built can be written + by hand, a simple function and a desire to make code cleaner. + + There are a lot of other things you can do with functions, really a + lot more I didn't mention or rather you can discover like uncharted + territory. + + *Use your imagination.* + +** The Power Of Functions In JavaScript + + If you forgot what I said, here it is. + + - Use =map=, =filter=, and =reduce= + - Learn =compose= and =curry= + - Think input and output, processes not details + - Avoid for loops and ifs + - Hide implementation details + - *Have fun* + +** What I Didn't Say + + Compare JavaScript with Haskell if you want to see the light + + - Partial Application + - Closures + The chapter I removed from this presentation + - Types + - Monads + - Functors + - Patterns + - So much + +** Future Of Functional Programming + + I am not an prophet but... + + - Functional Reactive Programming + - React & React Native + - Clojure / ClojureScript + - RxJs + - Elm + - Java 8: Lambdas + + Really, it's just a good time to learn a new paradigm to widen your + perspective. + +* Lastly + + No matter what I say or what paradigm is out there. Good code is + something you work for. There is really no silver bullet. + + Don't be a zealot. OOP and FP have their places in design and + coding. Use your judgment which works for the correct situation. + + But really in JavaScript, I find FP to be really easy to work with + than its Prototypical nature. + +* Now GTFO + + Thank you for letting me waste your time. I hope you learned something + or even just the idea. + +* References + +** Books + + - [[http://shop.oreilly.com/product/9780596517748.do][Javascript: The Good Parts]] + - [[http://shop.oreilly.com/product/0636920028857.do][Functional JavaScript]] + - [[http://shop.oreilly.com/product/9781784398224.do][Functional Programming In JavaScript]] + +** Libraries + + - [[https://lodash.com/][lodash]] - a good functional library + - [[http://jquery.com/][jquery]] - not obvious but it really is a monad + - [[https://github.com/Reactive-Extensions/RxJS][rxjs]] - functional reactive programming library + +** Languages + + - [[https://www.haskell.org/][haskell]] - the language drug to functional programming + - [[http://clojure.org/][clojure]] - the functional lisp since Scheme + - [[https://github.com/clojure/clojurescript][clojurescript]] - the lisp for javascript + - [[http://elm-lang.org/][elm]] - the functional javascript diff --git a/presentation.html b/presentation.html index 3debce1..42462d1 100644 --- a/presentation.html +++ b/presentation.html @@ -1690,12 +1690,32 @@

The Power Of Functions In JavaScript

https://github.com/FrancisMurillo/the-power-of-functions-in-javascript

-
-

Foolish Assumptions

+

If It Is After April. 14

+

+This is the original source of the presentation, the revised cut I +used for the presentation is the presentation-shortened.org. +

+ +

+I had hoped to be able to give this in 30 minutes but I failed to +realize that I think faster than I talk, showed too much code and +tried to discuss a wide range. A valiant effort. +

+ +

+I hope this presentation will help you write good code and with a +different lens. Thanks. +

+ +
+
+
+
+

Foolish Assumptions

  • You know the basics of JavaScript syntax
  • I am talking about JavaScript in the browser
  • @@ -1706,8 +1726,8 @@

    Foolish Assumptions

-
-

A Little Background

+
+

A Little Background

Just a mediocre software developer.

@@ -1715,8 +1735,8 @@

A Little Background

-
-

Before We Begin

+
+

Before We Begin

Judge a man by his questions rather than his answers @@ -1732,8 +1752,8 @@

Before We Begin

-
-

JavaScript Sucks

+
+

JavaScript Sucks

Where do I begin?

@@ -1748,17 +1768,17 @@

JavaScript Sucks

-

+

But I am not here to bash on the language.

-

+

How do we write good code in a (sucky but awesome) language?

-
-

Writing Good Code For JavaScript

+
+

Writing Good Code For JavaScript

How do write code that is…

@@ -1782,8 +1802,8 @@

Writing Good Code For JavaScript

-
-

Function

+
+

Function

The building blocks of programs

@@ -1799,8 +1819,8 @@

Function

-
-

Functions In JavaScript

+
+

Functions In JavaScript

The best thing about JavaScript is its implementation of functions. It @@ -1819,32 +1839,33 @@

Functions In JavaScript

-
-

Things You Should Know

+
+

Things You Should Know

Just a brief review of the following.

  • Functions
  • -
  • arguments keyword
  • +
  • this and arguments keyword
  • call and apply function
  • -
  • Closure / Function Scope
  • +
  • Closure
  • +
  • Recursion
-
-

Functions

+
+

Functions

The bread and butter of programmers

-
-

Declaring Functions

+
+

Declaring Functions

There are two primary ways to declare a function.

@@ -1860,12 +1881,17 @@

Declaring Functions

var greetPatty = function _greetPatty() { return 'Hello Sir'; }; + +// Function Constructor +// We never ever talk or even write this as this is as evil as eval() +// This is just for completion +var greetLinus = new Function ("return 'Sweet Baboo';");
-
-

Invoking Functions

+
+

Invoking Functions

There's three known ways to call or invoke a function.

@@ -1890,8 +1916,8 @@

Invoking Functions

-
-

Returning An Output

+
+

Returning Values

return is the output of the function. If there is no return, an implicit undefined is the output. @@ -1913,11 +1939,51 @@

Returning An Output

-
-

Convention

+
+

Gotcha: Function Hoisting

+

+It is the compiler putting variable/function declarations on top(without your +consent) +

+ +

+Consider this snippet consisting of a function statement +

+ +
+ +
doWeirdStuff()
+
+function doWeirdStuff() {
+    console.log('Wait, this is declared after it is invoked.');
+}
+
+
+ +

+Compare this with the function expression equivalent +

+ +
+ +
doWeirdStuff()
+
+var doWeirdStuff = function theWeirdStuff() {
+    console.log('This is the same right? It should also work. Right?');
+}
+
+
+ +

+The first one executes, while the other one gives a undefined type error. +

+ +
+
+

Tip

-For this presentation, the preferred way of declaring a function is a -function expression to avoid function hoisting. +Prefer function expression over function statement to avoid +hoisting.

@@ -1931,29 +1997,58 @@

Convention

-
-

arguments

+
+

this and arguments

-Every function has an implicit variable arguments +Every function has two implicit variable: this and arguments

var MyModule = {
-    whatIsThis: function (/* args */) {
+    whatIsThis: function () {
+        // What is?
+        console.log(this);
+
         // And what is
         console.log(arguments);
     }
 }
 
 MyModule.whatIsThis(1, 2, 3, 4);
+// out(this): MyModule
 // out(arguments): [1, 2, 3, 4]
 
-
-

My arguments

+
+

Just this

+

+It is just simply the method's object +

+ +
+ +
var MyObject= {
+
+    myValue: 'Nyan Cat',
+
+    myMethod: function () {
+        console.log(this);
+        console.log(this.myValue);
+    }
+};
+
+MyObject.myMethod();
+// out(this): { myValue: 'Nyan Cat', myMethod: function ()}
+// out(this.myValue): 'Nyan Cat'
+
+
+ +
+
+

My arguments

An array-like object containing the arguments

@@ -1969,38 +2064,145 @@

My arguments

showMeTheEvidence(1, 2, 3) // out: [1,2,3] showMeTheEvidence('This', 'is', 'nuts') // out: ['This', 'is', 'nuts'] + +// Not enough arguments? That' weirdly okay. Javascript is free +showMeTheEvidence({ x: 1 }, true); +// out([a, b, c]): [{x:1}, true, undefined] +// out(arQguments): [{x:1}, true] + +// Too many arguments? That's free of charge too +showMeTheEvidence(true, false, 0, 1) +// out([a, b, c]): [true, false, 0] +// out(arguments): [true, false, 0, 1]
-
-

Convention

+
+

Gotcha: arguments is not an array

+

+It is not an array +

+ +
+ +
var extraRicePlease = function (/* args */) {
+    var args = arguments;
+
+    args.push('Extra rice');
+
+    console.log(args);
+};
+
+extraRicePlease('BBQ', 'Ice Tea');
+// expected: ['BBQ', 'ice Tea', 'Extra Rice']
+// out: arguments has no method push
+
+
+ +
+
+

Tip

-Whenever I depend on the arguments keyword, I put a comment on the -function header what the local variable name would be and convert it -into an array using Array.prototype.slice.call(arguments) or the -shorter [].slice.call(arguments). +Convert arguments to an array using Array.prototype.slice.call(arguments).

var extraRicePlease = function (/* args */) {
-    var args = [].slice.call(arguments); // This is awkward
+    var args = Array.prototype.slice.call(arguments); // Why must we do this
 
-    args.push('Extra rice'); // Adding arguments
+    args.push('Extra rice'); // Now it works
 
     console.log(args);
 };
 
-extraRicePlease('BBQ', 'Ice Tea'); // out: ['BBQ', 'Ice Tea', 'Extra Rice']
+extraRicePlease('BBQ', 'Ice Tea');
+// out: ['BBQ', 'Ice Tea', 'Extra Rice']
+
+
+ +

+Use can also use the shorter form [].slice.call(arguments). +

+ +
+
+

Gotcha: Function Scoped

+

+Check out this sketch that counts something globally and locally. +

+ +
+ +
var Counter = {
+    globalCounter: 0,
+
+    createCounter: function() {
+        return {
+            localCounter: 0,
+
+            increment: function() {
+                this.globalCounter += 1;
+                this.localCounter += 1;
+
+                return this.localCounter;
+            }
+        }
+    }
+};
+
+var buttonCounter = Counter.createCounter(),
+    ajaxCounter = Counter.createCounter();
+
+console.log(buttonCounter.increment()); // out: 1
+console.log(ajaxCounter.increment()); // out: 1
+
+
+console.log(Counter.globalCounter);
+// expected: 2;
+// out: 0;
+
+
+ +
+
+

Tip

+

+Be careful using this. If you use functions within functions or +objects within objects, it is safer to save it in a variable +preferrably that or the Pythonic self. Likewise for arguments +but very uncommon. +

+ +
+ +
var Counter = {
+    globalCounter: 0,
+
+    createCounter: function() {
+        var that = this; // Store the parent refrence
+
+        return {
+            localCounter: 0,
+
+            increment: function() {
+                that.globalCounter += 1; // Correct reference
+                this.localCounter += 1;
+
+                return this.localCounter;
+            }
+        }
+    }
+};
 
-
-

f.call() and f.apply()

+
+

f.call() and f.apply()

Every function has the method call and apply to invoke them functionally

@@ -2018,31 +2220,144 @@

f.call() and f.apply()

// Using call() without the console.log(fullName.call(null, 'Mr', 'Robot')); // out: 'Mr Robot' + // Using apply() console.log(fullName.apply(null, ['Jake', 'Dog'])); // out: 'Jake Dog'
-
-

Convention

+
+

this Argument

+

+The first argument of call and apply sets the this +keyword, this is more important for the OO style. +

+ +
+ +
var Mirror = {
+    whoAmI: 'Tyler Durden',
+
+    whoAreYou: function (prefix) {
+        console.log(prefix + this.whoAmI);
+    }
+};
+
+var Bad = {
+    whoAmI: 'Heisenberg'
+};
+
+Mirror.whoAreYou('I am '); // out: 'I am Tyler Durden'
+
+Mirror.whoAreYou.call(Bad, 'You are '); // out: 'You are Heisenberg'
+
+var VeryBad = {
+    whoAmI: 'You only need to know mine'
+};
+
+Mirror.whoAreYou.apply(VeryBad, ['I do not need to know your name']); // out: awesome
+
+
+ +
+
+

Practical this

+

+Some practical uses of setting the this argument. +

+ +
+ +
var showcaseThis = function (/* args */) {
+    var args = arguments;
+
+    // Case: Converting arguments to an array, reasonable
+    console.log(Array.prototype.slice.call(this, args));
+};
+
+showcaseThis('Clockwork', 'Orange');
+// out: ['Clockwork', 'Orange']
+
+// Case: Invoking a "class"/prototype method on an instance
+// Why not just "Beethoven".toUpperCase()?
+console.log(String.prototype.toUpperCase("Beethoven"));
+// out: BEETHOVEN
+
+// Easier to just ['Ode'],concat('to', 'Joy');
+console.log(Array.prototype.concat.call(['Ode'],'to', 'Joy'));
+// out: ['Ode', 'to', 'Joy']
+
+// Case: Hacking an "private" variable
+// Wait for it
+
+
+ +
+
+

Gotcha: Missing this

+

+Consider this quaint way of invoking a method +

+ +
+ +
var Cat = {
+    affection: 9007199254740992;, // It is at least over 9000
+
+    pet: function _pettingTheAdorablePuspin() {
+        if (this.affection > 50) {
+            console.log('purr');
+        } else {
+            console.log('hiss');
+        }
+    }
+};
+
+var petMethod = Cat.pet;
+
+// Working at the office
+// Home and tired, I want to relieve stress. Must pet cat
+
+petMethod();
+// expected: 'purr'
+// out: 'hiss'
+
+
+ +
+
+

Tip

-call and apply will always have null as the first argument -since we don't care about this. +Be aware if a function needs an object reference or not.

+
+ +
petMethod(); // out: 'hiss'
+// Because this.affection is undefined
+// undefined > 50 is false
+// This should be a type error!?
+
+petMethod.call(Cat); // out: 'purr'
+
+// If you know the object, then why not just invoke it directly
+Cat.pet();
+
+
+

-apply is the more preferred way of calling functions if you don't -know the arguments, and call if you do. +For this presentation, we will not use this and invoke call and +apply with null as the first argument whenever needed.

-
-

Closure / Function Scope

+
+

Closure

-Inner functions have access to it's the variables of the outer function +Functions have access to variables to it's outer function.

@@ -2069,46 +2384,183 @@

Closure / Function Scope

-
-
-
-
-

The Power Of Functions In JavaScript

-

-Good design is not about making grand plans, but about taking things -apart. +We'll talk about this more later

+
+
+

Gotcha: Memory Leaks

-– Rich Hickey +Not really going to talk about this, but it exists.

- -

-At last, let's talk about functions. -

+
-
-
-

Functional Programming In JavaScript

-

-I don't want to bore you with a definition. It's really just a mindset -

+
setInterval(function myLovelyWidget() {
+    var data = []; // Possibly never garbage collected
 
-
    -
  • Input and Output
  • -
  • Combining Functions
  • -
  • Process over Step
  • -
  • Everything is a Function
  • -
  • Avoiding State
  • + fetch('someUrl') // Promise like behavior + .then(function getAndRender(newData) { + data = newData; -
+ // render new data + + // do something else + }); +}, 1000); +
+
-
-

Why I Like Functional Programming?

-
    +
+
+
+

Recursion

+

+Calling a function over and over again +

+ +
+ +
// Naive quicksort
+var quicksort = function _quicksorter(xs) {
+    if (xs.length <= 1) { // Base cases
+        return xs
+    } else if (xs.length === 2) {
+        var first = xs[0],
+            last = xs[1];
+
+        return first <= last ? [first, last] : [last, first];
+    } else { // Dividing the task
+        var pivot = xs[0],
+            less = xs.filter(function (x) { return x < pivot; }), // Spoiler Alert
+            more = xs.filter(function (x) { return x > pivot; }),
+            equal = xs.filter(function (x) { return x === pivot; });
+
+        return []
+            .concat(_quicksorter(less))
+            .concat(equal)
+            .concat(_quicksorter(more));
+    }
+};
+
+console.log(quicksort([4, 1, 2, 3])); // out: [1, 2, 3, 4]
+
+
+ +
+
+

Calling The Caller

+

+Invoking the parent or calling function using the name given in the +function declaration. +

+ +
+ +
var factorial = function _factorer(n) { // Notice the name in the function
+    if (n <= 1) { // Base case
+        return 1;
+    } else { // Induction case
+        return n * _factorer(n - 1); // Invoke the parent function
+        // return n * factorial(n - 1); // Also okay but relies on external binding
+    }
+};
+
+console.log(factorial(3)); // out: 3 * 2 * 1 => 6
+console.log(factorial(5)); // out: 5 * 4 * 3 * 2 * 1 => 120
+
+
+ +
+
+

Gotcha: Infinite Recursion

+

+Be very careful within recursion as it can cause an infinite recursion with +rogue or incorrect input. +

+ +
+ +
var countDown = function _counter(n) {
+    if (n === 0) { // Base case
+        console.log('Blast off!!');
+    } else {
+        console.log(n);
+        counter(n - 1); // Recursion
+    }
+};
+
+countDown(3);
+// out: 3
+// out: 2
+// out: 1
+// out: 'Blast off!!'
+
+countDown(3.001); // Unhandled case
+// out: 3.001
+// out: 2.001
+// out: 1.001
+// out: 0.001 // Where's the smoke?
+// out: -0.999 // Wait... when is the rocket launching?
+// out: -1.999 // Houston, we have a problem
+
+
+ +
+
+

Tip

+

+Since JavaScript does not support functional programming up front, +just be careful when choosing to implement a problem with recursion. +

+ +

+For this presentation, I won't delve into recursion but for just +know and remember you can do recursion… with love and care. +

+ +
+
+
+
+

The Power Of Functions In JavaScript

+
+

+Good design is not about making grand plans, but about taking things +apart. +

+ +

+– Rich Hickey +

+
+ +

+At last, let's talk about functions. +

+ +
+
+

Functional Programming In JavaScript

+

+I don't want to bore you with a definition. It's really just a mindset +

+ +
    +
  • Input and Output
  • +
  • Combining Functions
  • +
  • Process over Step
  • +
  • Everything is a Function
  • +
  • Avoiding State
  • + +
+ +
+
+

Why I Like Functional Programming?

+
  • First principles
  • Easier to write good code
  • Easier to reason
  • @@ -2118,8 +2570,30 @@

    Why I Like Functional Programming?

-
-

What This Is Not

+
+

Why Not OOP?

+
+

+Christianity has not been tried and found wanting; it has been found +difficult and not tried. +

+ +

+– Gilbert Chersterton +

+
+ +
    +
  • State
  • +
  • Classes are heavyweight
  • +
  • Don't know the prototype system
  • +
  • Overrated
  • + +
+ +
+
+

What This Is Not

  • A full course in this paradigm
  • A pure functional style
  • @@ -2129,8 +2603,8 @@

    What This Is Not

-
-

What This Is

+
+

What This Is

To introduce you to think and write in a functional style can help you write good code in JavaScript and nothing more. @@ -2139,8 +2613,8 @@

What This Is

-
-

The Idea

+
+

The Idea

What is the most resilient parasite? Bacteria? A virus? An intestinal @@ -2156,8 +2630,8 @@

The Idea

-
-

The Ideas Of Functional Programming For JavaScript

+
+

The Ideas Of Functional Programming For JavaScript

The whole of the presentation

@@ -2165,29 +2639,23 @@

The Ideas Of Functional Programming For JavaScript

  • Thinking in terms of collections or as a whole
  • Separating behaviors and combining them
  • -
  • map, filter and reduce
  • -
  • compose and curry
  • +
  • Avoiding state with closures
-
-

Caveat

+
+

Caveat

You do not have to understand the code, just feel and see that this is much cleaner and better as a whole.

-

-The idea is more important, ignore the nuances of the language but -see that it is easily done. -

-
-
-

Thinking With Functions

+
+

Chapter 1: Thinking With Functions

Let's talk about lists and their functions namely

@@ -2206,8 +2674,8 @@

Thinking With Functions

-
-

A Question Of Intent

+
+

A Question Of Intent

Who here uses a for loop like so?

@@ -2223,8 +2691,8 @@

A Question Of Intent

-
-

What's Wrong With For Loops?

+
+

What's Wrong With For Loops?

Intention

@@ -2255,8 +2723,8 @@

What's Wrong With For Loops?

-
-

No More For Loops

+
+

No More For Loops

Our journey starts by using the forEach method of an list which allows you to loop through a list given a callback. @@ -2285,8 +2753,8 @@

No More For Loops

-
-

0th Virtue: Function Variables

+
+

0th Virtue: Function Variables

The bare minimum that allows JavaScript to be functional is that functions are first class objects meaning you can pass functions as @@ -2313,8 +2781,8 @@

0th Virtue: Function Variables

-
-

Implementing forEach

+
+

Implementing forEach

Let's implement our own forEach for analysis

@@ -2345,10 +2813,10 @@

Implementing forEach

-
-

Transforming Collections

+
+

Transforming Collections

-Let's move on a similar case of transforming a collection. +Let's move on a similar case of transforming the collection.

@@ -2377,8 +2845,8 @@

Transforming Collections

-
-

The Intention

+
+

The Intention

The intention of the problem if we define it as a function.

@@ -2396,8 +2864,8 @@

The Intention

-
-

Using map

+
+

Using map

Thankfully the map method of lists allows this.

@@ -2412,11 +2880,10 @@

Using map

// Just like forEach var newWords = words.map(capitalize); - // or if you want to inline it -var newWordsInline = words.map(function _inlineCapitalize(word) { - return text.toUpperCase(); -}); +// var newWords = words.map(function (word) { +// return text.toUpperCase(); +// }); console.log(newWords); // Like the last @@ -2427,8 +2894,8 @@

Using map

-
-

Examples of map

+
+

Examples of map

Just some examples to get improve comprehension

@@ -2463,8 +2930,8 @@

Examples of map

-
-

Implementing map

+
+

Implementing map

Just like with forEach, implenting the map is easy.

@@ -2500,8 +2967,8 @@

Implementing map

-
-

1st Virtue: Input And Output

+
+

1st Virtue: Input And Output

The user of the function should only be concerned with the input and output, not how it is done. It is transforming the inputs to produce @@ -2515,16 +2982,13 @@

1st Virtue: Input And Output

-What, not how is the thinking style here. -

- -

-The idea of transforming or mapping is demonstrated in our next concept +This is both hard to explain and do. Let's demonstrate this with the +next concept.

-
-

Objects As Collections

+
+

Objects As Collections

You can think of objects as a collection of key value pairs

@@ -2550,8 +3014,8 @@

Objects As Collections

-
-

Pairs

+
+

Pairs

Pairs are just lists with two elements. Easy, right?

@@ -2569,8 +3033,8 @@

Pairs

-
-

Objects As Pairs

+
+

Objects As Pairs

Let's create a function to convert an object to a list of key value pairs @@ -2610,8 +3074,8 @@

Objects As Pairs

-
-

Using toPairs

+
+

Using toPairs

Just a demonstration.

@@ -2643,8 +3107,8 @@

Using toPairs

-
-

Pairs As Objects

+
+

Pairs As Objects

Let's create a function to do the opposite: convert a function

@@ -2655,14 +3119,13 @@

Pairs As Objects

var set = function (key, value, object) { object[key] = value; - return object; // Remember to return the object + return object; }; var fromPairs = function (pairs) { // The recursive version is much more functional but not necessarily better var newObject = {}; - // Relies on side effect to get it done pairs.forEach(function (pair) { var key = pair[0], value = pair[1]; @@ -2675,13 +3138,13 @@

Pairs As Objects

-

+

So what does it mean to think of objects as pairs?

-
-

Objects As A Whole

+
+

Objects As A Whole

What if you had an object and want to capitalize every string?

@@ -2722,8 +3185,8 @@

Objects As A Whole

-
-

Implementing mapObject

+
+

Implementing mapObject

Using all we made so far

@@ -2752,8 +3215,8 @@

Implementing mapObject

-
-

Another Example of mapObject

+
+

Another Example of mapObject

What if we want to find the differences between an old and new object? @@ -2786,21 +3249,8 @@

Another Example of mapObject

-
-

Reflection: forEach and map

-

-So far we just separated the intent from our for loop and we came up -with a neat little behavior and we were able to apply it to -objects. -

- -

-Cool, so what's next? -

- -
-
-

Shrinking Collections

+
+

Shrinking Collections

Let's move on to filtering collections given a criteria.

@@ -2831,33 +3281,8 @@

Shrinking Collections

-
-

Predicate Functions

-

-Before we use filter, a bit of terminology. -

- -

-A predicate function is just a function that returns true or -false -

- -
- -
// Predicate functions
-var equal = function (a, b)  { return a === b; },
-    isOdd = function (n) { return n % 2 === 0; },
-    alwaysTrue = function () { return true; }; // Always returns true
-
-// Not predicate functions
-var toString = function (x) { return x.toString(); }, // Text value
-    concat = function (xs, ys) { return xs.concat(ys); }; // List value
-
-
- -
-
-

Using filter

+
+

Using filter

Given a predicate or a boolean function and a list, return a new list where the predicate is true over the list. @@ -2887,8 +3312,8 @@

Using filter

-
-

Implementing filter

+
+

Implementing filter

Again, we implement this for our own benefit.

@@ -2906,8 +3331,6 @@

Implementing filter

ys.push(y); } }); - - return ys; }; var isLowerCase = function (text) { @@ -2920,22 +3343,27 @@

Implementing filter

-

+

So we have another trick to use with lists. However, this trick has two other concepts attached to it.

-
-

First Thing: Nested Ifs

+
+

First Thing: Nested Ifs

Who here likes nested if statements?

-
var a === true,
-    b === false;
+
var a === true;
+
+if (a) {
+    // One if is okay
+}
+
+var b === false;
 
 if (!a) {
     if (b) {
@@ -2962,8 +3390,8 @@ 

First Thing: Nested Ifs

-
-

Code Review

+
+

Code Review

Consider this code as an example of nesting ifs. Given a list of books that are to be processed and a book isbn table, find all the @@ -3003,8 +3431,8 @@

Code Review

-
-

Refactor 01: Separate Intentions

+
+

Refactor 01: Separate Intentions

Separate the main intentions

@@ -3039,8 +3467,8 @@

Refactor 01: Separate Intentions

-
-

Refactor 02: Use Filter

+
+

Refactor 02: Use Filter

Let's remove the first if statement using the new found filter power

@@ -3079,8 +3507,8 @@

Refactor 02: Use Filter

-
-

Refactor 03: Use Filter Again

+
+

Refactor 03: Use Filter Again

We can chain the filter to remove the other if

@@ -3118,8 +3546,8 @@

Refactor 03: Use Filter Again

-
-

Refactor 04: Just filter

+
+

Refactor 04: Just filter

We just let the filter chain be the result and we're done

@@ -3153,8 +3581,8 @@

Refactor 04: Just filter

-
-

Avoiding Ifs With filter

+
+

Avoiding Ifs With filter

Notice how the code is much more readable and easier to understand without the explicit ifs? @@ -3180,8 +3608,8 @@

Avoiding Ifs With filter

-
-

Second Thing: Filter Collections

+
+

Second Thing: Filter Collections

If objects are also collections, then couldn't we filter over them as well? @@ -3220,8 +3648,8 @@

Second Thing: Filter Collections

-
-

Implementing filterObject

+
+

Implementing filterObject

I can't believe this is too easy.

@@ -3243,8 +3671,8 @@

Implementing filterObject

-
-

Using filterObject

+
+

Using filterObject

How about a contrived example?

@@ -3280,21 +3708,8 @@

Using filterObject

-
-

Reflection: filter

-

-So we gained a new capability to filter a list and it applies to -objects as well. Again this stems from the fact that we are just -refactoring. -

- -

-This leads us to the next virtue -

- -
-
-

2nd Virtue: Data Abstraction

+
+

2nd Virtue: Data Abstraction

By thinking about the operations over data, we can abstract the behavior to other containers. @@ -3317,8 +3732,8 @@

2nd Virtue: Data Abstraction

-
-

Collecting Collections

+
+

Collecting Collections

Finally, we move on to collecting or aggregating all the values of an collection.

@@ -3351,8 +3766,8 @@

Collecting Collections

-
-

Using reduce

+
+

Using reduce

reduce takes a combining function and a list and returns the combined values of the list. @@ -3382,11 +3797,10 @@

Using reduce

-
-

Implementing reduce

+
+

Implementing reduce

-Once you implement it, the idea of combining function is easy. Again -this is just the code above that is just refactored. +Once you implement it, the idea of combining function is easy

@@ -3409,16 +3823,15 @@

Implementing reduce

-
-

Examples of reduce

+
+

Examples of reduce

Some basic examples of reduce

-
// I like Math
-var numbers = [1, 2, 3, 4];
+
var numbers = [1, 2, 3, 4];
 
 var multiply = function (x, y)  {
     return x * y;
@@ -3434,8 +3847,8 @@ 

Examples of reduce

-
-

A More Specific find

+
+

A More Specific find

Sometimes in a list, you want to find a specific value given a criteria or predicate. @@ -3470,8 +3883,8 @@

A More Specific find

-
-

A Deep Getter

+
+

A Deep Getter

Imagine you have an deep object and you want to access an inner object and don't want to deal with undefined types exception. We can @@ -3505,8 +3918,8 @@

A Deep Getter

-
-

What Does reduce Mean For Us

+
+

What Does reduce Mean For Us

If you have a list and want an aggregated value, the reduce should be a natural choice. It allows you think in terms of combining the @@ -3518,22 +3931,8 @@

What Does reduce Mean For Us

-
-

Reflection: reduce

-

-So we now have a tool that aggregates or combines list, we might as -well have the same thing for objects. -

- -

-We now have a trio of tools and it came naturally due to a need to -separate intention. Nothing complex has been done so far which is -impressive what a simple function can do. -

- -
-
-

All Together Now

+
+

Combining All Three

Given a list of people, find all the people that are not minors and compute the total. @@ -3559,7 +3958,7 @@

All Together Now

return x + y } -console.log(people // Wow +console.log(people .filter(isNotAMinor) .map(getSalary) .reduce(add)); // Nice to see the three working together @@ -3567,8 +3966,8 @@

All Together Now

-
-

Quick Review

+
+

Quick Review

Just a quick review of everything we've done.

@@ -3584,10 +3983,10 @@

Quick Review

-
-

Combining Functions

+
+

Chapter 2: Combining Functions

-Let's get to the fun stuff. +Let's get to the fun stuff

@@ -3601,21 +4000,21 @@

Combining Functions

-
-

3rd Virtue: Functions As Units Of Behavior

+
+

3rd Virtue: Functions As Units Of Behavior

Functions represents behavior or what we have been calling intentions.

-Primarily, a function should represent an idea and not be tangled +Primarily, a function should represent a process and not be tangled with other details

-
-

A Simple Event

+
+

A Simple Event

Let's say we have two buttons with their corresponding handlers.

@@ -3637,8 +4036,8 @@

A Simple Event

-
-

Adding A Counter

+
+

Adding A Counter

Now, what if we want to track send information to the server that a button was clicked? Let's say we have a sendClick function that @@ -3670,8 +4069,8 @@

Adding A Counter

-
-

Simple Function Over Functions

+
+

Simple Function Over Functions

What we want is to separate the handler from the logging. What we want is to execute a function before the main one. @@ -3701,8 +4100,8 @@

Simple Function Over Functions

-
-

Using doBefore

+
+

Using doBefore

Let's see how it makes the code much better

@@ -3715,7 +4114,7 @@

Using doBefore

console.log('This button does something awesome'); }); // This functions returns the combined function -var otherButtonHandler = doBefore(sendClick, function () { // Ditto +var otherButtonHandler = doBefore(sendClick, function () { console.log('This button does something way better than the other one'); }); @@ -3728,8 +4127,8 @@

Using doBefore

-
-

Reflection: Higher Order Functions

+
+

Reflection: Higher Order Functions

How many times have you copy pasted a block of function to several places without consideration for it's purpose or intent? @@ -3746,8 +4145,8 @@

Reflection: Higher Order Functions

-
-

Another Warm Up: Preventing Execution

+
+

Another Warm Up: Preventing Execution

What if instead of executing before, we prevent the execution of the next one? @@ -3789,8 +4188,8 @@

Another Warm Up: Preventing Execution

-
-

One More Appetizer: Preventing Too Many Clicks

+
+

One More Appetizer: Preventing Too Many Clicks

How about we prevent a button being pressed in a short burst, could be useful for submit buttons @@ -3819,7 +4218,7 @@

One More Appetizer: Preventing Too Many Clicks

} }; -var buttonHandler = debounce(function _actualHandler() { +var buttonHandler = debound(function _actualHandler() { console.log('You just paid some website some electronic cash'); }, 300); // 300 is a magic number @@ -3831,8 +4230,8 @@

One More Appetizer: Preventing Too Many Clicks

-
-

Learning To Compose

+
+

Learning To Compose

Once you understand that you can join functions together, you can create a pipeline using functions. @@ -3871,8 +4270,8 @@

Learning To Compose

-
-

A Messy Invokation

+
+

A Messy Invokation

Just to demonstrate the extra parenthesis are not just dirty, but harder to read and maintain @@ -3905,8 +4304,8 @@

A Messy Invokation

-
-

Introducting compose

+
+

Introducting compose

So the function we're looking for compose, given two functions it will call the second one with the arguments and pass it's output to @@ -3930,8 +4329,6 @@

Introducting compose

var nextValue = outer.call(null, firstValue); // Then invoke the outer function with the return value as the argument - // or which looks natural // return outer(inner.apply(null, arguments)); - return nextValue; }; }, @@ -3943,8 +4340,8 @@

Introducting compose

-
-

Using compose

+
+

Using compose

Let's join those two functions in with compose

@@ -3967,18 +4364,18 @@

Using compose

// ... }; -console.log(objectSerialize(data)); // Ah, looks cleaner. +console.log(objectSerialize(data));

We successfully removed those wonky parenthesis but how else can -compose help us in our journey of better code? +compose help us in our journey of better code.

-
-

Examples of compose

+
+

Examples of compose

Let's get acquainted with our new friend, make it your BFF if you can. @@ -4000,12 +4397,13 @@

Examples of compose

-Composing is better when you start using it with currying. But for -now, how do we compose more than two functions? +Composing is better when you start using it with closures and +currying. But for now, how do we compose more than two functions?

+
-
-

Improving compose

+
+

Improving compose

So how do we compose functions all the way to the moon?

@@ -4037,8 +4435,8 @@

Improving compose

-
-

Some Things First

+
+

Some Backups

Let's implement a few things to improve the readability of our grand composes @@ -4075,8 +4473,8 @@

Some Things First

-
-

One More Thing

+
+

One More Thing

I prefer to read left to right instead of right to left with my function pipeline. @@ -4105,8 +4503,8 @@

One More Thing

-
-

Implementing composes

+
+

Implementing composes

With that out of the way, we simply use reduceRight and pipe in one master stroke @@ -4124,13 +4522,12 @@

Implementing composes

-It is just basically starting at the end and composing all the way -back. That's how easy it is to implement composes. +That's how easy it is to implement composes.

-
-

Equivalently pipes

+
+

Equivalently pipes

Just for the sake of symmetry.

@@ -4150,8 +4547,8 @@

Equivalently pipes

-
-

Checking Out composes

+
+

Checking Out composes

Let's check it out.

@@ -4180,8 +4577,8 @@

Checking Out composes

-
-

Function Pipelines

+
+

Function Pipelines

It's not the fact that you use compose or pipe, but rather that you want your code to be a single and straight process. @@ -4204,8 +4601,8 @@

Function Pipelines

-
-

Avoiding Ifs With Composition

+
+

Avoiding Ifs With Composition

Let's have another case review. Given a list of employees and there salary, let's compute their take home pay. @@ -4233,8 +4630,8 @@

Avoiding Ifs With Composition

-
-

Gender Inequality

+
+

Gender Inequality

Now what if every female employee gets paid 50% more due to a goverment law. What do you do to make it right? @@ -4270,8 +4667,8 @@

Gender Inequality

-
-

Composition To The Rescue

+
+

Composition To The Rescue

Let's use composition to make the code cleaner.

@@ -4306,8 +4703,8 @@

Composition To The Rescue

-
-

Reflection: Compose Behaviors

+
+

Reflection: Compose Behaviors

Although the example is quite contrived, the main point is to avoid complexity and maintain the simplicity of the process. @@ -4324,21 +4721,8 @@

Reflection: Compose Behaviors

-
-

Reflection: composes

-

-So we just implemented one of the cornerstones of functional -programming and it wasn't complicated. All we wanted was to remove -the dirty parenthesis, amazing. -

- -

-Let's go learn the complimentary technique for composition. -

- -
-
-

Learning To Curry

+
+

Learning To Curry

Currying is a nice functional idea to sweeten composition

@@ -4373,8 +4757,8 @@

Learning To Curry

-
-

The Neat Trick

+
+

The Neat Trick

Let's have another example with a function that takes three arguments

@@ -4404,8 +4788,8 @@

The Neat Trick

-
-

Naming The Trick

+
+

Naming The Trick

But this seems to be a function behavior, not logic. Maybe we can separate this behavior. @@ -4440,8 +4824,8 @@

Naming The Trick

-
-

Currying For Two

+
+

Currying For Two

Copy pasting our trick from before

@@ -4469,8 +4853,8 @@

Currying For Two

-
-

Implementing curry

+
+

Implementing curry

This is the most fun and challenging to code but the main concept is to keep returning a function until enough arguments have been @@ -4508,8 +4892,8 @@

Implementing curry

-
-

Using curry

+
+

Using curry

Just a contrived use of the generic curry

@@ -4541,8 +4925,8 @@

Using curry

-
-

The Real Trick

+
+

The Real Trick

Curry a function until it has one argument left which you can pass through a composition or mapping pipeline. @@ -4577,8 +4961,8 @@

The Real Trick

-
-

Currying And Filtering

+
+

Currying And Filtering

How about using it in conjuction with filter? Given a list of heroes, I want to filter the intelligence type heroes in the list or team. @@ -4617,8 +5001,44 @@

Currying And Filtering

-
-

I Want My Modules Curried

+
+

Fixed Options

+

+Let's say you have a third party component componentBuilder and +the a web page has a whole lot of it. You can curry the builder with +the options required and let it be the function that builds it to +serve as a function interface. +

+ +
+ +
// The third party builder function
+var componentBuilder = function (options, element) {
+    /* ... */
+};
+
+// The options for the page
+var pageOptions = {
+    /* ... */
+}
+
+// I don't care about the options, just build it when I give it to you
+var buildComponent = curry(componentBuilder)(pageOptions);
+
+var domComponents = [
+    document.getElementById('period'),
+    document.getElementById('type'),
+    document.getElementById('projects')
+];
+
+// Just build it for each of this
+domComponents.forEach(buildComponent);
+
+
+ +
+
+

I Want My Modules Curried

I love currying functions that all my functions in my module should be curried by default. @@ -4649,110 +5069,664 @@

I Want My Modules Curried

-
-

Reflection: curry

+
+

4th Virtue: Everything Should Be A Function

-Another cornerstone has been implemented. We now have the ability to -create functions from old one. +With higher ordered concepts like composition and currying, if +everything is a function then all of them benefit from higher order +abstractions.

-But the real question is: are there more tricks like this? +Now you know why get and set are functions so that they can be +curried and composed.

+

+There are other higher order functions that sweeten the deal. +

-
-

4th Virtue: Everything Should Be A Function

+
+

Quick Review

-With higher ordered concepts like composition and currying, if -everything is a function then all of them benefit from higher order -abstractions. +So those are my fundamental tricks.

+
    +
  • compose, pipe and curry are fun tools to implement and have
  • +
  • Combining functions are easy
  • +
  • Making new functions from old are easy
  • + +
+ +
+
+
+
+

Chapter 3: State Of Functions

-There are other higher order functions that sweeten the deal. +This last section is much more concerned with the philosophy of this paradigm

-Now you know why get and set are functions so that they can be -curried and composed. +Spoilers

+
    +
  • Closures
  • +
  • Immediately Invoked Function Expressions(IIFE)
  • + +
+
-
-

Currying Our Old Trick

+
+

A Question Of Design

-Tying this back to our old previous tools +Does anyone know the output of this snippet?

-
var characters = [
-    { id: 314, name: 'Dilbert', source: 'Dilbert'},
-    { id: 319, name: 'Catbert', source: 'Dilbert'},
-    { id: 325, name: 'Sally', source: 'Peanuts'}
-];
+
var games = ['Tetris', 'Pong', 'Mortal Kombat'];
 
-var getId = curry(get)('id'),
-    getIds = curry(map)(getId); // Currying our implementation
+// Sort the list
+var sortedGames = games.sort());
 
-console.log(getIds(characters)); // out: [314, 319, 325]
+console.log(sortedGames);
+
+
-var isFromPeanuts = curry(get)('id'), - fromPeanuts = curry(filter)(isFromPeanuts); +

+The sorted list but there is something else afoot here. +

-console.log(fromPeanuts(characters)); // out: [{id:325,name:'Sally',source:'Peanuts'}] +
-var getIdByPeanuts = pipe(isFromPeanuts, getIds); +
console.log(games === games.sort());
+
+
-console.log(getIdByPeanuts(characters)); // out: [325] +

+This returns true. The list is sorted in place, so they're the +same. This is what is known as a side effect. +

+ +
+
+

Side Effects

+

+This is simply something that changes when the function is +invoked. This is also known as state mutation. +

+ +
+ +
var counter = 0;
+
+var sum = function (numbers) {
+    counter += 1; // State mutation or side effect
+
+    return reduceFirst(function (a, b) { return a + b; }, numbers);
+};
+
+console.log(counter); // out: 0
+
+// We didn't pass in counter...
+console.log(sum([1,2,3])); // out: 6
+
+// ... yet why is it changed without my conset
+console.log(counter); // out: 1
 
+
+
+

A Function Of Trust And Reason

-If this did not impress you, I don't know what will. +How do you know if a function has side effects? How do you know if +the parameters aren't manipulated or changed without your +consent? +

+ +

+The main point is that it is harder to reason about code or +understand when it changes something outside it's scope or +function. +

+ +

+Side effects aren't bad, we just want to minimize it if we can.

-
-

Functional Promise

+
+

5th Virtue: (Almost) No Side Effects

+

+Functions should (almost) never do the following +

+ +
    +
  • Mutate the parameters
  • +
  • Mutate global state
  • +
  • If you do mutate, make it explicit or known
  • +
  • If you do need state, we have closures for that
  • + +
+ +

+This is the hardest to follow. If you notice yourself making state or +mutating variables, consider the consequences.. +

+ +
+
+

Closures: Revisited

-If you design your functions correctly, your code will look -declarative instead of imperative. +What closures really are is that they remember the environment, scope +or variables.

+
+ +
var rememberThis = function (value) {
+    var rememberMe = value; // This value will exist until function does
+
+    return function () {
+        return rememberMe; // It remembers me
+    };
+};
+
+var rememberTheDate  = rememberThis('Fifth Of November');
+
+// Several bad puns later
+
+console.log(rememberTheDate()); // out: Fifth Of November
+
+
+

-Aside from aesthetic value, it is more readable, easier to write, and -will put a smile on your face. +Here is the fun bit: can you change the value of rememberMe directly?

-
-

Quick Review

+
+

Closures And State

-So those are my fundamental tricks. +Closures are really state for functions. +

+ +
+ +
var incrementer = function (startValue) {
+    var currentValue = startValue; // Function state
+
+    return function () {
+        // The value is increased each time it is called
+        currentValue += 1; // Mutate the state
+
+        return currentValue;
+    };
+};
+
+var increasingSequence = incrementer(0);
+
+console.log(increasingSequence()); // out: 1
+console.log(increasingSequence()); // out: 2
+console.log(increasingSequence()); // out: 3
+
+
+ +

+This is a form of state mutation that is well contained within the +function. Nobody can access currentValue directly. +

+ +

+You can create lazy sequences with this technique. +

+ +
+
+

Private Variables With Closure

+

+Closures can provide what is known as private variables to OOP +

+ +
+ +
// Creating an entity with function closure
+var person = function (initialName, initialGender) {
+    var currentName = initialName,
+        currentGender = initialGender;
+
+    // This time we return an object that interacts with the closure
+    return {
+        getName: function () { return currentName; },
+        getGender: function () { return currentGender; },
+
+        // Prefer to make a new copy but to show state here
+        setName: function (newName) { currentName = newName; },
+
+        // There is no change gender function
+    };
+};
+
+var me = function ('FnMurillo', '♂');
+
+console.log(me.getName()); // out: FnMurillo
+console.log(me.getGender()); // out: ♂
+
+// Who I really am
+me.setName('Batman');
+
+// It changed!!
+console.log(me.getName()); // out: Batman
+
+
+ +
+
+

Data Isolation

+

+Although we can implement private variables with closures, what we +want is to hide it as much as possible. The closure should be the +only one to modify it state, there's really no point in hiding it in +a closure if you are going to reveal it anyway, an object would do +the job instead. +

+ +

+This is the opposite of data encapsulation. +

+ +

+Let's talk about a small definition to where closure applies easily +

+ +
+
+

Idempotent Functions

+

+Fancy words saying functions that always give the same output for the +same input. +

+ +
+ +
var twice = function (n) { return n * 2; }; // Math, very idempotent
+
+// Weird function that adds whatever you put in last with the current
+var addPrevious = function () {
+    var previous = 0;
+
+    return function (n) {
+        oldPrevious = previous; // Store old value
+
+        previous = n; // Mutate value
+
+        return oldPrevious + n; // Do the math;
+    }
+}
+
+var adder = addPrevious();
+
+console.log(adder(5)); // out: 5   // oldPrevious = 0, newPrevious = 5
+// Does not return the same value
+console.log(adder(5)); // out: 10  // oldPrevious = 5, newPrevious = 10
+
+var hiddenState = 0,
+    decrement = function (n) {
+        hiddenState += 1;
+        return n - 1;
+    };
+
+// This has side effects but it is idempotent
+console.log(decrement(4)); // out: 4 // hiddenState = 1
+console.log(decrement(4)); // out: 4 // hiddenState = 2
+
+
+ +
+
+

Memoization

+

+Just another short demonstration using closure. If a function is +idempotent, you can cache the values. Caching for functions basically +

+ +
+ +
// Memoization for one argument function
+var memoize = function (f) {
+    var cacheTable = {}; // Closure state, note that this hidden all throughout
+
+    return function (n) { // Implemented for only one argument
+        var cachedValue = get(n, cacheTable); // Get cached value
+
+        if (cachedValue === undefined) { // If there is no cached value
+            cachedValue = f.call(null, n); // Invoke the function
+            set(n, cachedValue, cacheTable); // Store the value
+        }
+
+        return cachedValue;
+    }
+};
+
+var encrypt = memoize(function (text) {
+    var veryLongEncryptionAlgorithm = function _sha1Maybe(text) { /* ... */};
+
+    return veryLongEncryptionAlgorithm(text);
+});
+
+
+ +

+Again, this is just to show that closures are supposed to hide +state. +

+ +
+
+

Starting With Functions

+

+There is a nice feature or trick in JavaScript known as Immediately +Invoked Function Expression or IIFE for short. +

+ +

+This just creating a function and invoking immediately. +

+ +
+ +
var y = (function _iife() { // Just create a function
+    return 1; // Why do this?
+}()); // ... and wrap it with an open close parenthesis
+
+var z = 1; // Why not just do this instead?
+
+
+ +

+This is cute, but what is is good for? Since it is a function, it has +closure; so anything you put inside it, remains inside. +

+ +
+
+

Creating A Namespace

+

+When creating scripts for JavaScript, it's good to wrap the whole +script as an IIFE to prevent and variable leaks since the default +scope of variables is global. +

+ +
+ +
(function _importMyCustomScript() {
+    var myScriptVar = 1;
+
+    // Assume the global namespace for my app is XYZ
+    XYZ.myFunction = function () { return myScriptVar; };
+}());
+
+
+ +

+This goes hand in hand with creating a global namespace if you are +working with the browser but we're not really going to dwell into that. +

+ +
+
+

Creating A Module

+

+You can also create a module using IIFE with this pattern. +

+ +
+ +
// Using IIFE to make modules
+var FP = (function () {
+    var hiddenVarInModule = 1;
+
+    // Object.create allows us to create truly empty object
+    var Module = Object.create(null); // or {} is enough if you don't care
+
+    // Object.freeze makes an object readonly
+    return Object.freeze(Module); // or Module if you don't care
+    // Return the module
+}());
+
+
+ +
+
+

Starting An Idea

+

+Not the best use, but when you are trying to sketch a function and +can't decide if it can be refactored. Starting with IIFE can get you +started. +

+ +
+ +
var originalFunction = function () {
+    // Blah blah blah
+
+    // New feature, need a better name for this function
+    (function _newFeature() { // If this is used again, it is easier to refactor
+        // Everything I do is within this bubble
+
+        var x = 1,
+            y= 'a';
+
+        // Do more stuff
+    }())
+
+
+    // Blah blah blah
+};
+
+
+ +

+I usually sketch the prototype within an IIFE, and keep it there to +give it a good indication of what it does. Then if it used again, I +just turn into an legit function and refactor. +

+ +
+
+

Stateful Talk

+

+Hiding state. +

+ +

+This is what closures should ultimately do. But what if you had to? +Here are some examples. +

+ +
+
+

Objects As Stateful Value

+

+JavaScript has blessed syntax for object creation: easy and flexible +to use. +

+ +
+ +
var myObject = {
+    myText: '',
+    myValue: null,
+    mySubObject: {
+        isEmpty: true
+    },
+};
+
+// It's actuall okay to mutate
+// NOTE: A comment to say that it mutates
+var newObject = set('myText', 'xyz', myObject);
+
+console.log(myObject.myText); // out: 'xyz'
+// but
+console.log(newObject === myObject); // out: true
+
+
+ +

+I put an object in, I get an object out. I don't care if you mutated +it, but be very mindful or explicit about this. Comments are your +friends. Using objects as a data format is a solid way to go about it. +

+ +

+One of your friends too in this field is known as Immutability which +might be a different topic. +

+ +
+
+

State As A Value

+

+What if given a big list of numbers, compute the average; what if +you could loop through it once? How would you do it? +

+ +
+ +
var bigDoop = [1, 2, 3, /* ... */, 10000000]; // Just a very big set
+
+// What appears to be more cumbersome
+var computeAverage = function (numbers) {
+    var getSum = curry(get)(0),
+        getCount = curry(get)(1);
+
+    var totals = numbers.reduce(function (prev, number) {
+        var currentSum = getSum(prev),
+            currentCount = getCount(prev);
+
+        return [currentSum + number, currentCount + 1];
+    }, [0, 0]); // Setting the initial state
+
+    var sum = get(0, totals),
+        count = get(1, totals);
+
+    return sum / count;
+};
+
+console.log(computeAverage(bigDoop)); // out: More numbers than Iron Man
+
+
+ +

+Notice the use of a lists to pass in state, the count and the +sum. If you're in a pipeline, it is much better to make the state +explicit as a value for all to see and inspect. +

+ +
+
+

State As An Argument

+

+This is not really practical but just to get your attention. Let's +implement a fancy recursive factorial implementation. +

+ +
+ +
// Avoid doing this, this is not helpful
+var factorial = function (n) {
+    var factorialHelper = function _recurse(n, value) {
+        if (n <= 0) { // If done, return the value
+            return value;
+        } else { // Else, do it again with the new state
+            return _recurse(n - 1, value * n); // This is unhealthy to pass state
+        }
+    };
+
+    return factorialHelper(n, 1); // Call with initial state
+};
+
+var factorialWhile = function (n) {
+    var value = 1;
+
+    while (n > 0) {
+        value *= n;
+        n-= 1;
+    }
+
+    return value;
+}
+
+
+ +

+If JavaScript was tail optimized, we would really do more of this +but it scares me to do it. But this is just to show the weird way to +pass in state. +

+ +
+
+

Managing State Is Hard

+

+In reality, hiding state is hard. +

+ +

+Being explicit about state almost translates to more code; this is +what can put you off to declare that global variable or just hack the +state variables. +

+ +

+I suggest paying the explicit code than someone paying for it +implicitly. +

+ +
+
+

Know Thy Closure

+

+Despite all that rant about state, if JavaScript didn't have closure, +I wouldn't know what to do about my work and there really wouldn't be +a functional paradigm for it. +

+ +

+Imagine if you could access all the variables inside a function? +

+ +
+
+

Quick Review

+

+Have I made my point?

    -
  • compose, pipe and curry are fun tools to implement and have
  • -
  • Combining functions are easy
  • -
  • Making new functions from old are easy
  • +
  • Closures are underrated in managing state
  • +
  • State makes code more complex, hide if you must
  • +
  • Objects and lists make excellent function data interchange
-
-

JavaScript Is Functional

+
+

Epilogue: JavaScript Is Functional

-End of the line. Wrap it up fool. +End of the line.

-
-

JavaScript Is Awesome

+
+

JavaScript Is Awesome

JavaScript is really cool for me because of the following.

@@ -4774,26 +5748,8 @@

JavaScript Is Awesome

-
-

Reflection: Refactoring

-

-You could have noticed that all the machinery we built can be written -by hand, a simple function and a desire to make code cleaner. -

- -

-There are a lot of other things you can do with functions, really a -lot more I didn't mention or rather you can discover like uncharted -territory. -

- -

-Use your imagination. -

- -
-
-

The Power Of Functions In JavaScript

+
+

The Power Of Functions In JavaScript

If you forgot what I said, here it is.

@@ -4804,21 +5760,19 @@

The Power Of Functions In JavaScript

  • Think input and output, processes not details
  • Avoid for loops and ifs
  • Hide implementation details
  • +
  • Hide state as much as possible
  • Have fun
  • -
    -

    What I Didn't Say

    +
    +

    What I Didn't Say

    Compare JavaScript with Haskell if you want to see the light

      -
    • Partial Application
    • -
    • Closures -The chapter I removed from this presentation
    • Types
    • Monads
    • Functors
    • @@ -4828,10 +5782,10 @@

      What I Didn't Say

    -
    -

    Future Of Functional Programming

    +
    +

    Future Of Functional Programming

    -I am not an prophet but… +I am not an Oracle but…

      @@ -4852,8 +5806,8 @@

      Future Of Functional Programming

    -
    -

    Lastly

    +
    +

    Lastly

    No matter what I say or what paradigm is out there. Good code is something you work for. There is really no silver bullet. @@ -4872,8 +5826,8 @@

    Lastly

    -
    -

    Now GTFO

    +
    +

    Now GTFO

    Thank you for letting me waste your time. I hope you learned something or even just the idea. @@ -4882,12 +5836,12 @@

    Now GTFO

    -
    -

    References

    -
    +
    +

    References

    +
    -
    -

    Books

    +
    +

    Books

    -
    -

    Libraries

    +
    +

    Libraries

    • lodash - a good functional library
    • jquery - not obvious but it really is a monad
    • @@ -4906,8 +5860,8 @@

      Libraries

    -
    -

    Languages

    +
    +

    Languages

    • haskell - the language drug to functional programming
    • clojure - the functional lisp since Scheme
    • diff --git a/presentation.org b/presentation.org index ae19b42..598c02d 100644 --- a/presentation.org +++ b/presentation.org @@ -41,6 +41,17 @@ *https://github.com/FrancisMurillo/the-power-of-functions-in-javascript* +* If It Is After April. 14 + + This is the original source of the presentation, the revised cut I + used for the presentation is the *presentation-shortened.org*. + + I had hoped to be able to give this in 30 minutes but I failed to + realize that I think faster than I talk, showed too much code and + tried to discuss a wide range. A valiant effort. + + I hope this presentation will help you write good code and with a + different lens. Thanks. * Foolish Assumptions @@ -74,8 +85,10 @@ - Monkey patching - The rest couldn't fit on one slide +#+ATTR_REVEAL: :frag t But I am not here to bash on the language. +#+ATTR_REVEAL: :frag t How do we write good code in a (sucky but awesome) language? ** Writing *Good Code* For JavaScript @@ -123,9 +136,10 @@ Just a brief review of the following. - Functions - - =arguments= keyword + - =this= and =arguments= keyword - =call= and =apply= function - - Closure / Function Scope + - Closure + - Recursion * Functions @@ -145,6 +159,11 @@ var greetPatty = function _greetPatty() { return 'Hello Sir'; }; + + // Function Constructor + // We never ever talk or even write this as this is as evil as eval() + // This is just for completion + var greetLinus = new Function ("return 'Sweet Baboo';"); #+END_SRC ** Invoking Functions @@ -168,7 +187,7 @@ add.apply(null, [1, 2]); #+END_SRC -** Returning An Output +** Returning Values =return= is the output of the function. If there is no =return=, an implicit =undefined= is the output. @@ -186,10 +205,37 @@ console.log(noReturn()); // out: undefined #+END_SRC -** Convention +** Gotcha: Function Hoisting + + It is the compiler putting variable/function declarations on top(without your + consent) + + Consider this snippet consisting of a *function statement* + + #+BEGIN_SRC javascript + doWeirdStuff() + + function doWeirdStuff() { + console.log('Wait, this is declared after it is invoked.'); + } + #+END_SRC + + Compare this with the *function expression* equivalent + + #+BEGIN_SRC javascript + doWeirdStuff() + + var doWeirdStuff = function theWeirdStuff() { + console.log('This is the same right? It should also work. Right?'); + } + #+END_SRC + + The first one executes, while the other one gives a *undefined type error*. - For this presentation, the preferred way of declaring a function is a - *function expression* to avoid function hoisting. +*** Tip + + Prefer *function expression* over *function statement* to avoid + hoisting. #+BEGIN_SRC javascript var theLongerWay = function isTheSaferWay() { @@ -197,22 +243,47 @@ }; #+END_SRC -* =arguments= +* =this= and =arguments= - Every function has an implicit variable *arguments* + Every function has two implicit variable: *this* and *arguments* #+BEGIN_SRC javascript var MyModule = { - whatIsThis: function (/* args */) { + whatIsThis: function () { + // What is? + console.log(this); + // And what is console.log(arguments); } } MyModule.whatIsThis(1, 2, 3, 4); + // out(this): MyModule // out(arguments): [1, 2, 3, 4] + #+END_SRC +** Just =this= + + It is just simply the method's object + + #+BEGIN_SRC javascript + var MyObject= { + + myValue: 'Nyan Cat', + + myMethod: function () { + console.log(this); + console.log(this.myValue); + } + }; + + MyObject.myMethod(); + // out(this): { myValue: 'Nyan Cat', myMethod: function ()} + // out(this.myValue): 'Nyan Cat' + #+END_SRC + ** My =arguments= An array-like object containing the arguments @@ -227,27 +298,117 @@ showMeTheEvidence(1, 2, 3) // out: [1,2,3] showMeTheEvidence('This', 'is', 'nuts') // out: ['This', 'is', 'nuts'] + + // Not enough arguments? That' weirdly okay. Javascript is free + showMeTheEvidence({ x: 1 }, true); + // out([a, b, c]): [{x:1}, true, undefined] + // out(arQguments): [{x:1}, true] + + // Too many arguments? That's free of charge too + showMeTheEvidence(true, false, 0, 1) + // out([a, b, c]): [true, false, 0] + // out(arguments): [true, false, 0, 1] + #+END_SRC + +** Gotcha: =arguments= is not an array + + It is not an array + + #+BEGIN_SRC javascript + var extraRicePlease = function (/* args */) { + var args = arguments; + + args.push('Extra rice'); + + console.log(args); + }; + + extraRicePlease('BBQ', 'Ice Tea'); + // expected: ['BBQ', 'ice Tea', 'Extra Rice'] + // out: arguments has no method push #+END_SRC -** Convention +*** Tip - Whenever I depend on the =arguments= keyword, I put a comment on the - function header what the local variable name would be and convert it - into an array using =Array.prototype.slice.call(arguments)= or the - shorter =[].slice.call(arguments)=. + Convert =arguments= to an array using =Array.prototype.slice.call(arguments)=. #+BEGIN_SRC javascript var extraRicePlease = function (/* args */) { - var args = [].slice.call(arguments); // This is awkward + var args = Array.prototype.slice.call(arguments); // Why must we do this - args.push('Extra rice'); // Adding arguments + args.push('Extra rice'); // Now it works console.log(args); }; - extraRicePlease('BBQ', 'Ice Tea'); // out: ['BBQ', 'Ice Tea', 'Extra Rice'] + extraRicePlease('BBQ', 'Ice Tea'); + // out: ['BBQ', 'Ice Tea', 'Extra Rice'] + #+END_SRC + + Use can also use the shorter form =[].slice.call(arguments)=. + +** Gotcha: Function Scoped + + Check out this sketch that counts *something* globally and locally. + + #+BEGIN_SRC javascript + var Counter = { + globalCounter: 0, + + createCounter: function() { + return { + localCounter: 0, + + increment: function() { + this.globalCounter += 1; + this.localCounter += 1; + + return this.localCounter; + } + } + } + }; + + var buttonCounter = Counter.createCounter(), + ajaxCounter = Counter.createCounter(); + + console.log(buttonCounter.increment()); // out: 1 + console.log(ajaxCounter.increment()); // out: 1 + + + console.log(Counter.globalCounter); + // expected: 2; + // out: 0; #+END_SRC +*** Tip + + Be careful using =this=. If you use functions within functions or + objects within objects, it is safer to save it in a variable + preferrably =that= or the Pythonic =self=. Likewise for =arguments= + but very uncommon. + + #+BEGIN_SRC javascript + var Counter = { + globalCounter: 0, + + createCounter: function() { + var that = this; // Store the parent refrence + + return { + localCounter: 0, + + increment: function() { + that.globalCounter += 1; // Correct reference + this.localCounter += 1; + + return this.localCounter; + } + } + } + }; + #+END_SRC + * =f.call()= and =f.apply()= Every function has the method =call= and =apply= to invoke them functionally @@ -264,21 +425,117 @@ // Using call() without the console.log(fullName.call(null, 'Mr', 'Robot')); // out: 'Mr Robot' + // Using apply() console.log(fullName.apply(null, ['Jake', 'Dog'])); // out: 'Jake Dog' #+END_SRC -** Convention +** =this= Argument + + The first argument of =call= and =apply= sets the =this= + keyword, this is more important for the OO style. + + #+BEGIN_SRC javascript + var Mirror = { + whoAmI: 'Tyler Durden', + + whoAreYou: function (prefix) { + console.log(prefix + this.whoAmI); + } + }; + + var Bad = { + whoAmI: 'Heisenberg' + }; + + Mirror.whoAreYou('I am '); // out: 'I am Tyler Durden' + + Mirror.whoAreYou.call(Bad, 'You are '); // out: 'You are Heisenberg' + + var VeryBad = { + whoAmI: 'You only need to know mine' + }; + + Mirror.whoAreYou.apply(VeryBad, ['I do not need to know your name']); // out: awesome + #+END_SRC + +** Practical =this= + + Some practical uses of setting the =this= argument. + + #+BEGIN_SRC javascript + var showcaseThis = function (/* args */) { + var args = arguments; + + // Case: Converting arguments to an array, reasonable + console.log(Array.prototype.slice.call(this, args)); + }; + + showcaseThis('Clockwork', 'Orange'); + // out: ['Clockwork', 'Orange'] + + // Case: Invoking a "class"/prototype method on an instance + // Why not just "Beethoven".toUpperCase()? + console.log(String.prototype.toUpperCase("Beethoven")); + // out: BEETHOVEN + + // Easier to just ['Ode'],concat('to', 'Joy'); + console.log(Array.prototype.concat.call(['Ode'],'to', 'Joy')); + // out: ['Ode', 'to', 'Joy'] + + // Case: Hacking an "private" variable + // Wait for it + #+END_SRC + +** Gotcha: Missing =this= + + Consider this quaint way of invoking a method + + #+BEGIN_SRC javascript + var Cat = { + affection: 9007199254740992;, // It is at least over 9000 + + pet: function _pettingTheAdorablePuspin() { + if (this.affection > 50) { + console.log('purr'); + } else { + console.log('hiss'); + } + } + }; + + var petMethod = Cat.pet; - =call= and =apply= will always have =null= as the first argument - since we don't care about =this=. + // Working at the office + // Home and tired, I want to relieve stress. Must pet cat - =apply= is the more preferred way of calling functions if you don't - know the arguments, and =call= if you do. + petMethod(); + // expected: 'purr' + // out: 'hiss' + #+END_SRC + +*** Tip + + Be aware if a function needs an object reference or not. + + #+BEGIN_SRC javascript + petMethod(); // out: 'hiss' + // Because this.affection is undefined + // undefined > 50 is false + // This should be a type error!? + + petMethod.call(Cat); // out: 'purr' + + // If you know the object, then why not just invoke it directly + Cat.pet(); + #+END_SRC -* Closure / Function Scope + For this presentation, we will not use =this= and invoke =call= and + =apply= with null as the first argument whenever needed. - Inner functions have access to it's the variables of the outer function +* Closure + + Functions have access to variables to it's outer function. #+BEGIN_SRC javascript var outerFunction = function (outerArg) { @@ -302,6 +559,114 @@ // out: [10, 20, 100, 120]; #+END_SRC + We'll talk about this more later + +** Gotcha: Memory Leaks + + Not really going to talk about this, but it exists. + + #+BEGIN_SRC javascript + setInterval(function myLovelyWidget() { + var data = []; // Possibly never garbage collected + + fetch('someUrl') // Promise like behavior + .then(function getAndRender(newData) { + data = newData; + + // render new data + + // do something else + }); + }, 1000); + #+END_SRC + +* Recursion + + Calling a function over and over again + + #+BEGIN_SRC javascript + // Naive quicksort + var quicksort = function _quicksorter(xs) { + if (xs.length <= 1) { // Base cases + return xs + } else if (xs.length === 2) { + var first = xs[0], + last = xs[1]; + + return first <= last ? [first, last] : [last, first]; + } else { // Dividing the task + var pivot = xs[0], + less = xs.filter(function (x) { return x < pivot; }), // Spoiler Alert + more = xs.filter(function (x) { return x > pivot; }), + equal = xs.filter(function (x) { return x === pivot; }); + + return [] + .concat(_quicksorter(less)) + .concat(equal) + .concat(_quicksorter(more)); + } + }; + + console.log(quicksort([4, 1, 2, 3])); // out: [1, 2, 3, 4] + #+END_SRC + +** Calling The Caller + + Invoking the parent or calling function using the name given in the + function declaration. + + #+BEGIN_SRC javascript + var factorial = function _factorer(n) { // Notice the name in the function + if (n <= 1) { // Base case + return 1; + } else { // Induction case + return n * _factorer(n - 1); // Invoke the parent function + // return n * factorial(n - 1); // Also okay but relies on external binding + } + }; + + console.log(factorial(3)); // out: 3 * 2 * 1 => 6 + console.log(factorial(5)); // out: 5 * 4 * 3 * 2 * 1 => 120 + #+END_SRC + +** Gotcha: Infinite Recursion + + Be very careful within recursion as it can cause an infinite recursion with + rogue or incorrect input. + + #+BEGIN_SRC javascript + var countDown = function _counter(n) { + if (n === 0) { // Base case + console.log('Blast off!!'); + } else { + console.log(n); + counter(n - 1); // Recursion + } + }; + + countDown(3); + // out: 3 + // out: 2 + // out: 1 + // out: 'Blast off!!' + + countDown(3.001); // Unhandled case + // out: 3.001 + // out: 2.001 + // out: 1.001 + // out: 0.001 // Where's the smoke? + // out: -0.999 // Wait... when is the rocket launching? + // out: -1.999 // Houston, we have a problem + #+END_SRC + +*** Tip + + Since JavaScript does not support functional programming up front, + just be careful when choosing to implement a problem with recursion. + + For this presentation, I won't delve into recursion but for just + know and remember you can do recursion... with love and care. + * The Power Of Functions In JavaScript #+BEGIN_QUOTE @@ -331,6 +696,20 @@ - Natural and Mathematical - *Fun and Creative* +** Why Not OOP? + + #+BEGIN_QUOTE + Christianity has not been tried and found wanting; it has been found + difficult and not tried. + + -- Gilbert Chersterton + #+END_QUOTE + + - State + - Classes are heavyweight + - Don't know the prototype system + - *Overrated* + ** What This Is Not - A full course in this paradigm @@ -361,26 +740,23 @@ - Thinking in terms of collections or as a whole - Separating behaviors and combining them - - =map=, =filter= and =reduce= - - =compose= and =curry= + - Avoiding state with closures ** Caveat You do not have to understand the code, just feel and see that this is much cleaner and better as a whole. - The idea is more important, ignore the nuances of the language but - see that it is easily done. - -* Thinking With Functions +* Chapter 1: Thinking With Functions Let's talk about lists and their functions namely Spoilers - =.forEach= - - =.map()= - - =.filter= + - =.map()= & =.mapObject= + - =.toPairs= & =.fromPairs= + - =.filter= & =.filterObject= - =.reduce= ** A Question Of Intent @@ -428,7 +804,7 @@ // Use forEach to iterate thorugh a list assassins.forEach(function getConnor(assassin) { // Give the function a descriptive name - if (assassin === 'Connor') { + if (hero === 'Connor') { console.log('Where is Charles Lee'); } }); @@ -471,7 +847,7 @@ for (var i = 0; i < xs.length; i+=1) { var x = xs[i]; - f(x); + f.call(this, x); } }; @@ -488,7 +864,7 @@ ** Transforming Collections - Let's move on a similar case of transforming a collection. + Let's move on a similar case of transforming the collection. How about given a list of text, capitalize each one. @@ -534,11 +910,10 @@ // Just like forEach var newWords = words.map(capitalize); - // or if you want to inline it - var newWordsInline = words.map(function _inlineCapitalize(word) { - return text.toUpperCase(); - }); + // var newWords = words.map(function (word) { + // return text.toUpperCase(); + // }); console.log(newWords); // Like the last #+END_SRC @@ -616,13 +991,222 @@ return a transformed list. You should not care how it did it, be it a for loop or a forEach function or a recursion. - *What*, not *how* is the thinking style here. -** Reflection: =forEach= and =map= + This is both hard to explain and do. Let's demonstrate this with the + next concept. + +** Objects As Collections + + You can think of objects as a collection of key value pairs + + #+BEGIN_SRC javascript + var object = { + 'cowboy': 'bebop', + 'stein': 'gate', + 'summer': 'wars' + }; + + var objectPairs = [ + ['cowboy', 'bebop'], + ['stein', 'gate'], + ['summer', 'wars'] + ]; + #+END_SRC + + So why should it matter? Patience, let's define what pairs are. + +*** Pairs + + *Pairs* are just lists with two elements. Easy, right? + + #+BEGIN_SRC javascript + var numberPair = [100, 20], + stringPair = ['Satoshi Kon', 'Tokyo Godfathers'], + keyValuePair = ['key', { subObject: 1}], + pairInPairs = [[1, 2], [3, 4]]; // Not going to go that far + + var numericTriple = [1, 2, 3], // Three elements + singleString = ['Working!!!']; // One element + #+END_SRC + +*** Objects As Pairs + + Let's create a function to convert an object to a list of key value + pairs + + #+BEGIN_SRC javascript + // Assuming we don't have Object.keys + // This returns the keys of an object + var keys = function (object) { + var keys = []; + + for (var key in object) { + if (object.hasOwnProperty(key)) { + keys.push(key); + } + } + + return keys; + }; + + // Making the get a function + var get = function (key, object) { + return object[key]; + }; + + // Converts an object to a list of key value pair + var toPairs = function (object) { + return keys(object) + .map(function covertToPair(key) { + var value = get(key, object); - So far we just separated the intent from our for loop and we came up - with a neat little behavior. + return [key, value]; + }); + }; + #+END_SRC + +*** Using toPairs + + Just a demonstration. + + #+BEGIN_SRC javascript + toPairs({ + number: 1, + text: 'meow', + isSomething: true, + + recurse: { + A: 'a', + B: 'b' + } + }) + /* + out: [ + ['number', 1], + ['text', 'meow'], + ['isSomething', true], + ['recurse', { + A: 'a', + B: 'b' + }]] + ] + ,*/ + #+END_SRC + +*** Pairs As Objects + + Let's create a function to do the opposite: convert a function + + #+BEGIN_SRC javascript + // Let's wrap the set of objects as a function + var set = function (key, value, object) { + object[key] = value; + + return object; + }; + + var fromPairs = function (pairs) { + // The recursive version is much more functional but not necessarily better + var newObject = {}; + + pairs.forEach(function (pair) { + var key = pair[0], + value = pair[1]; + + set(key, value, object); + }); + + return newObject; + }; + #+END_SRC + + #+ATTR_REVEAL: :frag t + So what does it mean to think of objects as pairs? + +*** Objects As A Whole + + What if you had an object and want to capitalize every string? + + What if we could map over over objects as we do with lists? + + #+BEGIN_SRC javascript + var formData = { + firstName: 'Linus', + lastName: 'Van Pelt', + age: 20 + }; + + var tryUppercase = function _intention(value) { + return (typeof value === 'string') ? value.toUpperCase() : value; + } + + var mapObject = function (f, object) { + // ??? + }; + + mapObject(tryUppercase, formData); + /* expected: + { + firstName: 'LINUS', + lastName: 'VAN PELT', + age: 20 + } + ,*/ + #+END_SRC + + #+ATTR_REVEAL: :frag t + Of course we can. + +*** Implementing =mapObject= - Cool, so what's next? + Using all we made so far + + #+BEGIN_SRC javascript + // A naive implementation to use all we have + var mapObject = function (f, object) { + var mappedPairs = toPairs(object) // Step 1: Convert to pairs + .map(function (pair) { + var key = pair[0], + value = pair[1], + newValue = f(value, key); // Step 2: Map over the value + + return [key, newValue]; // Can be more succinct + }); + + return fromPairs(mappedPairs); // Step 3: Convert to object + }; + #+END_SRC + + Notice how we transform the object to pairs and back again. This + demonstrates the first virtue. + +*** Another Example of =mapObject= + + What if we want to find the differences between an old and new + object? + + #+BEGIN_SRC javascript + var diff = function (oldObject, newObject) { + return mapObject(function (oldValue, key) { + var newValue = newObject[key]; + + return oldValue !== newValue; + }, oldObject); + } + + var oldData = { company: 'Outbreak Comapny', position: 'Ambassador', gender: 'male'}, + newData = { company: 'IT Outsourcing', position: 'Ambassador', gender: 'male'}; + + var diffData = diff(oldData, newData); + + console.log(diffData); + /* out: + { company: true, position: false, gender: false } + */ + + if (diffData.company) { // Compare with oldData.company !== newData.company + // Do something when the company changes + } + #+END_SRC ** Shrinking Collections @@ -647,24 +1231,6 @@ There is a function for lists called [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter][filter]] that can get our job done -*** Predicate Functions - - Before we use =filter=, a bit of terminology. - - A *predicate function* is just a function that returns =true= or - =false= - - #+BEGIN_SRC javascript - // Predicate functions - var equal = function (a, b) { return a === b; }, - isOdd = function (n) { return n % 2 === 0; }, - alwaysTrue = function () { return true; }; // Always returns true - - // Not predicate functions - var toString = function (x) { return x.toString(); }, // Text value - concat = function (xs, ys) { return xs.concat(ys); }; // List value - #+END_SRC - *** Using =filter= Given a predicate or a boolean function and a list, return a new @@ -705,8 +1271,6 @@ ys.push(y); } }); - - return ys; }; var isLowerCase = function (text) { @@ -718,13 +1282,22 @@ console.log(filter(isLowerCase, vendors)); // out: ['dahl'] #+END_SRC -** Nested Ifs + #+ATTR_REVEAL: :frag t + So we have another trick to use with lists. However, this trick + has two other concepts attached to it. + +** First Thing: Nested Ifs Who here likes nested =if= statements? #+BEGIN_SRC javascript - var a === true, - b === false; + var a === true; + + if (a) { + // One if is okay + } + + var b === false; if (!a) { if (b) { @@ -923,22 +1496,94 @@ more likely to add another filter to the chain and less likely to just hack the if condition. -** Reflection: =filter= - - So we gained a new capability to filter a list and it applies to - objects as well. Again this stems from the fact that we are just - refactoring. +** Second Thing: Filter Collections - This leads us to the next virtue - -** 2nd Virtue: Data Abstraction + If objects are also collections, then couldn't we filter over them as + well? - By thinking about the operations over data, we can abstract the - behavior to other containers. + For example, we want to remove all =null= or =undefined= in an object + so that we can pass it over the wire. #+BEGIN_SRC javascript - var f = function (x) { - // Do something with x + var ajaxData = { + email: 'francisavmurillo@gmail.com', + age: null, + gender: 'male', + birthday: null + } + + var isData = function (value) { + return value != null || value != undefined || value != ''; + }; + + console.log(filterObject(isData, ajaxData)); // filterObject??? + /* out: + { + email: 'francisavmurillo@gmail.com', + gender: 'male' + } + ,*/ + #+END_SRC + + #+ATTR_REVEAL: :frag t + You already know it is. + +*** Implementing =filterObject= + + I can't believe this is too easy. + + #+BEGIN_SRC javascript + var filterObject = function (p, object) { // Just like mapObject + var filteredPairs = toPairs(object) + .filter(function (pair) { // Notice map is just replaced with filter + var key = pair[0], + value = pair[1], + + return p(value, key); // We apply the predicate here + }); + + return fromPairs(filteredPairs); + } + #+END_SRC + +*** Using =filterObject= + + How about a contrived example? + + #+BEGIN_SRC javascript + var request = { + _method: 'POST', + _csrf: 'abcde', + someData: 1 + }; + + var isUnderscored = function (text) { + return text[0] === '_'; + }; + + + + console.log(filterObject(function (value, key) { + return isUnderscored(key); + }, request)); + + /* out: { + _method: 'POST', + _csrf: 'abcde' + } + ,*/ + #+END_SRC + + But filtering by keys is not my point. + +** 2nd Virtue: Data Abstraction + + By thinking about the operations over data, we can abstract the + behavior to other containers. + + #+BEGIN_SRC javascript + var f = function (x) { + // Do something with x } mapX(f, xs); // What is xs? What if xs is a Promise, an Observable @@ -958,12 +1603,12 @@ // You're probably getting the picture or getting bored var numbers = [1, 2, 3, 4, 5]; + var sum = 0; + var add = function (a, b) { return a + b; }; - var sum = 0; - numbers.forEach(function (number) { sum = add(sum, number); }); @@ -971,6 +1616,7 @@ console.log(sum); #+END_SRC + #+ATTR_REVEAL: :frag t We have the [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FArray%2FReduc][reduce]] function for this *** Using =reduce= @@ -999,8 +1645,7 @@ *** Implementing =reduce= - Once you implement it, the idea of combining function is easy. Again - this is just the code above that is just refactored. + Once you implement it, the idea of combining function is easy #+BEGIN_SRC javascript var reduce = function (oper, initialValue, xs) { @@ -1017,12 +1662,11 @@ Let's have the usual examples -*** Example of =reduce= +*** Examples of =reduce= Some basic examples of =reduce= #+BEGIN_SRC javascript - // I like Math var numbers = [1, 2, 3, 4]; var multiply = function (x, y) { @@ -1064,18 +1708,48 @@ console.log(find(isTheMeaningOfLife, 0, [37, 43, 49])); // out: 0, the default value #+END_SRC -** Reflection: =reduce= +*** A Deep Getter + + Imagine you have an deep object and you want to access an inner + object and don't want to deal with undefined types exception. We can + make a safe getter for that. + + #+BEGIN_SRC javascript + var deepGetter = function (deepKey, object) { + var keys = deepKey.split('.'); - So we now have a tool that aggregates or combines list. + return keys.reduce(function _deepGet(currentObject, key) { + var newObject = get(key, currentObject); - We now have a trio of tools and it came naturally due to a need to - separate intention. Nothing complex has been done so far which is - impressive what a simple function can do. + return newObject === null || newObject === undefined ? + undefined : newObject; + }, object); + }; -** All Together Now + var deepObject = { + a: { + b: { + c: 1 + } + } + }; + + console.log(deepGetter('a.b.c', object)); // out: 1 + console.log(deepGetter('a.d', object)); // out: undefined + #+END_SRC + +*** What Does =reduce= Mean For Us + + If you have a list and want an aggregated value, the =reduce= should + be a natural choice. It allows you think in terms of combining the + values instead of hacking around at computing it. + + We'll get back to this function later. + +** Combining All Three Given a list of people, find all the people that are not minors and - compute the total salary. + compute the total. #+BEGIN_SRC javascript var people [ @@ -1096,7 +1770,7 @@ return x + y } - console.log(people // Wow + console.log(people .filter(isNotAMinor) .map(getSalary) .reduce(add)); // Nice to see the three working together @@ -1111,9 +1785,9 @@ - =map=, =filter= and =reduce= should be your best friends - Process over data -* Combining Functions +* Chapter 2: Combining Functions - Let's get to the fun stuff. + Let's get to the fun stuff Spoilers @@ -1125,7 +1799,7 @@ Functions represents behavior or what we have been calling intentions. - Primarily, a function should represent an idea and not be tangled + Primarily, a function should represent a process and not be tangled with other details *** A Simple Event @@ -1203,7 +1877,7 @@ console.log('This button does something awesome'); }); // This functions returns the combined function - var otherButtonHandler = doBefore(sendClick, function () { // Ditto + var otherButtonHandler = doBefore(sendClick, function () { console.log('This button does something way better than the other one'); }); #+END_SRC @@ -1223,6 +1897,75 @@ What we just did is make an *higher order function* but that's just definition. +** Another Warm Up: Preventing Execution + + What if instead of executing before, we prevent the execution of the + next one? + + For example, if you have a button and you want to allow the event to + continue if the user has enough privilege or what have you. + + #+BEGIN_SRC javascript + var executeIf = function (checkIf, main) { + return function (/* args */) { + var args = [].slice.call(arguments); + + // Check the condition + if (checkIf.apply(null, arguments) === true) { + return main.apply(null, arguments); + } + } + }; + + var checkWhatever = function (/* args */) { // Check for something + console.log('You shall not pass'); + // alert('You shall not pass'); // also works + + return false; + }; + + var buttonHandler = executeIf(checkWhatever, function () { + console.log('Button has been clicked'); + }); + #+END_SRC + + Again the code to glue the two functions together is cheap and easy. + +** One More Appetizer: Preventing Too Many Clicks + + How about we prevent a button being pressed in a short burst, could + be useful for submit buttons + + #+BEGIN_SRC javascript + var debounce = function (f, delay) { // I didn't come up with debounce + var blockInvokations = false; // Using closures here, spoilers + + return function (/* args */) { + var args = [].slice.call(arguments); + + if (blockInvokations === false) { + blockInvokations = true; // Block succeeding invokations + + setTimeout(function _unblock() { // Unblock it after the delay + blockInvokations = false; + }, delay); + + return f.apply(null, arguments); // Execute normally + } else { + // Can't execute yet, got to wait + return undefined; + } + } + }; + + var buttonHandler = debound(function _actualHandler() { + console.log('You just paid some website some electronic cash'); + }, 300); // 300 is a magic number + #+END_SRC + + A little more complicated but it does show how the we can separate + concerns or behaviors + ** Learning To Compose Once you understand that you can join functions together, you can @@ -1249,6 +1992,7 @@ var data = { name: 'Muffin', breed: 'Puspin', age: 4 }; var output = objectToJson(objectToUpperCase(data)); // The end result + // But I shouldn't care about toJson or toUpperCase #+END_SRC Better if the two functions can be joined as one intention, =objectSerialize=? @@ -1294,10 +2038,12 @@ return function (/* args */) { var args = arguments; - var innerValue = inner.apply(null, args), - outerValue = outer(firstValue); + var firstValue = inner.apply(null, args); + // First, invoke the inner function + + var nextValue = outer.call(null, firstValue); + // Then invoke the outer function with the return value as the argument - // or which looks natural // return outer(inner.apply(null, arguments)); return nextValue; }; }, @@ -1326,11 +2072,11 @@ // ... }; - console.log(objectSerialize(data)); // Ah, looks cleaner. + console.log(objectSerialize(data)); #+END_SRC We successfully removed those wonky parenthesis but how else can - compose help us in our journey of better code? + compose help us in our journey of better code. *** Examples of =compose= @@ -1350,8 +2096,8 @@ displaySquare = compose(display, square); #+END_SRC - Composing is better when you start using it with currying. But for - now, how do we compose more than two functions? + Composing is better when you start using it with closures and + currying. But for now, how do we compose more than two functions? ** Improving =compose= @@ -1379,7 +2125,7 @@ First, we go back to our old friends -*** Some Things First +*** Some Backups Let's implement a few things to improve the readability of our grand =composes= @@ -1449,8 +2195,7 @@ }; #+END_SRC - It is just basically starting at the end and composing all the way - back. That's how easy it is to implement =composes=. + That's how easy it is to implement =composes=. *** Equivalently =pipes= @@ -1594,14 +2339,6 @@ As a tip, whenever there is a new functionality or rule, consider composition or the like to manage the complexity. -** Reflection: =composes= - - So we just implemented one of the cornerstones of functional - programming and it wasn't complicated. All we wanted was to remove - the dirty parenthesis, amazing. - - Let's go learn the complimentary technique for composition. - ** Learning To Curry *Currying* is a nice functional idea to sweeten composition @@ -1776,7 +2513,6 @@ { name: 'Pyrrha Nikos', team: 'JNPR'} ]; - // Remember set takes three arguments: key, value, object var setHealth = curry(set)('hp', 100), setScore = curry(set)('score', 0); @@ -1827,77 +2563,549 @@ Still cool -** Reflection: =curry= +*** Fixed Options + + Let's say you have a third party component =componentBuilder= and + the a web page has a whole lot of it. You can curry the builder with + the options required and let it be the function that builds it to + serve as a function interface. + + #+BEGIN_SRC javascript + // The third party builder function + var componentBuilder = function (options, element) { + /* ... */ + }; + + // The options for the page + var pageOptions = { + /* ... */ + } - Another cornerstone has been implemented. We now have the ability to - create functions from old one. + // I don't care about the options, just build it when I give it to you + var buildComponent = curry(componentBuilder)(pageOptions); + + var domComponents = [ + document.getElementById('period'), + document.getElementById('type'), + document.getElementById('projects') + ]; + + // Just build it for each of this + domComponents.forEach(buildComponent); + #+END_SRC + +*** I Want My Modules Curried + + I love currying functions that all my functions in my module should be + curried by default. + + #+BEGIN_SRC javascript + var isTypeOf = curry(function (type, value) { + return typeof value === type; + }); + + var isFunction = isTypeOf('function'); + + + var invokeIf = curry(function (pred, f, value) { + return pred(value) ? f(value) : value; + }); + + + var autoCurry = curry(mapObject)(invokeIf(isFunction, curry)); + + var Module = autoCurry({ + doThis: function (a, b) { console.log([a, b]); } + }); + + Module.doThis(1)('b'); // out: [1, 'b'] + #+END_SRC - But the real question is: are there more tricks like this? ** 4th Virtue: Everything Should Be A Function With higher ordered concepts like composition and currying, if everything is a function then all of them benefit from higher order abstractions. - There are other higher order functions that sweeten the deal. - Now you know why =get= and =set= are functions so that they can be curried and composed. -** Currying Our Old Trick + There are other higher order functions that sweeten the deal. + +** Quick Review + + So those are my fundamental tricks. + + - =compose=, =pipe= and =curry= are fun tools to implement and have + - Combining functions are easy + - Making new functions from old are easy - Tying this back to our old previous tools +* Chapter 3: State Of Functions + + This last section is much more concerned with the philosophy of this paradigm + + Spoilers + + - Closures + - Immediately Invoked Function Expressions(IIFE) + +** A Question Of Design + + Does anyone know the output of this snippet? #+BEGIN_SRC javascript - var characters = [ - { id: 314, name: 'Dilbert', source: 'Dilbert'}, - { id: 319, name: 'Catbert', source: 'Dilbert'}, - { id: 325, name: 'Sally', source: 'Peanuts'} - ]; + var games = ['Tetris', 'Pong', 'Mortal Kombat']; + + // Sort the list + var sortedGames = games.sort()); + + console.log(sortedGames); + #+END_SRC + + #+ATTR_REVEAL: :frag t + The sorted list but there is something else afoot here. + + #+ATTR_REVEAL: :frag t + #+BEGIN_SRC javascript + console.log(games === games.sort()); + #+END_SRC + + #+ATTR_REVEAL: :frag t + This returns true. The list is sorted in place, so they're the + same. This is what is known as a *side effect*. + +*** Side Effects + + This is simply something that changes when the function is + invoked. This is also known as *state mutation*. + + #+BEGIN_SRC javascript + var counter = 0; + + var sum = function (numbers) { + counter += 1; // State mutation or side effect + + return reduceFirst(function (a, b) { return a + b; }, numbers); + }; + + console.log(counter); // out: 0 + + // We didn't pass in counter... + console.log(sum([1,2,3])); // out: 6 + + // ... yet why is it changed without my conset + console.log(counter); // out: 1 + #+END_SRC + +*** A Function Of Trust And Reason + + How do you know if a function has side effects? How do you know if + the parameters aren't manipulated or changed without your + consent? + + The main point is that it is harder to reason about code or + understand when it changes something outside it's scope or + function. + + Side effects aren't bad, we just want to minimize it if we can. - var getId = curry(get)('id'), - getIds = curry(map)(getId); // Currying our implementation +** 5th Virtue: (Almost) No Side Effects - console.log(getIds(characters)); // out: [314, 319, 325] + Functions should (almost) never do the following - var isFromPeanuts = curry(get)('id'), - fromPeanuts = curry(filter)(isFromPeanuts); + - Mutate the parameters + - Mutate global state + - If you do mutate, make it explicit or known + - If you do need state, we have closures for that - console.log(fromPeanuts(characters)); // out: [{id:325,name:'Sally',source:'Peanuts'}] + This is the hardest to follow. If you notice yourself making state or + mutating variables, consider the consequences.. - var getIdByPeanuts = pipe(isFromPeanuts, getIds); +** Closures: Revisited - console.log(getIdByPeanuts(characters)); // out: [325] + What closures really are is that they remember the environment, scope + or variables. + + #+BEGIN_SRC javascript + var rememberThis = function (value) { + var rememberMe = value; // This value will exist until function does + + return function () { + return rememberMe; // It remembers me + }; + }; + + var rememberTheDate = rememberThis('Fifth Of November'); + + // Several bad puns later + + console.log(rememberTheDate()); // out: Fifth Of November #+END_SRC - If this did not impress you, I don't know what will. + Here is the fun bit: can you change the value of =rememberMe= directly? -** Reflection: =curry= +*** Closures And State - We implemented one of the nice tricks of functional programming, we - can now extract functions from old one by presetting the - arguments. + Closures are really state for functions. - But the idea is that combining functions is really not that hard. How - many more ways can we play with functions? + #+BEGIN_SRC javascript + var incrementer = function (startValue) { + var currentValue = startValue; // Function state -** Quick Review + return function () { + // The value is increased each time it is called + currentValue += 1; // Mutate the state - So those are the fundamental tricks. + return currentValue; + }; + }; - - =compose=, =pipe= and =curry= are fun tools to implement and have - - Gluing functions are easy - - Making new functions from old + var increasingSequence = incrementer(0); + + console.log(increasingSequence()); // out: 1 + console.log(increasingSequence()); // out: 2 + console.log(increasingSequence()); // out: 3 + #+END_SRC + + This is a form of state mutation that is well contained within the + function. Nobody can access =currentValue= directly. + + You can create lazy sequences with this technique. + +*** Private Variables With Closure + + Closures can provide what is known as *private variables* to OOP + + #+BEGIN_SRC javascript + // Creating an entity with function closure + var person = function (initialName, initialGender) { + var currentName = initialName, + currentGender = initialGender; + + // This time we return an object that interacts with the closure + return { + getName: function () { return currentName; }, + getGender: function () { return currentGender; }, + + // Prefer to make a new copy but to show state here + setName: function (newName) { currentName = newName; }, + + // There is no change gender function + }; + }; + + var me = function ('FnMurillo', '♂'); + + console.log(me.getName()); // out: FnMurillo + console.log(me.getGender()); // out: ♂ + + // Who I really am + me.setName('Batman'); + + // It changed!! + console.log(me.getName()); // out: Batman + #+END_SRC + +*** Data Isolation + + Although we can implement private variables with closures, what we + want is to hide it as much as possible. The closure should be the + only one to modify it state, there's really no point in hiding it in + a closure if you are going to reveal it anyway, an object would do + the job instead. + + This is the opposite of *data encapsulation*. + + Let's talk about a small definition to where closure applies easily + +*** Idempotent Functions + + Fancy words saying functions that always give the same output for the + same input. + + #+BEGIN_SRC javascript + var twice = function (n) { return n * 2; }; // Math, very idempotent + + // Weird function that adds whatever you put in last with the current + var addPrevious = function () { + var previous = 0; + + return function (n) { + oldPrevious = previous; // Store old value + + previous = n; // Mutate value + + return oldPrevious + n; // Do the math; + } + } + + var adder = addPrevious(); + + console.log(adder(5)); // out: 5 // oldPrevious = 0, newPrevious = 5 + // Does not return the same value + console.log(adder(5)); // out: 10 // oldPrevious = 5, newPrevious = 10 + + var hiddenState = 0, + decrement = function (n) { + hiddenState += 1; + return n - 1; + }; + + // This has side effects but it is idempotent + console.log(decrement(4)); // out: 4 // hiddenState = 1 + console.log(decrement(4)); // out: 4 // hiddenState = 2 + #+END_SRC + +*** Memoization + + Just another short demonstration using closure. If a function is + idempotent, you can cache the values. Caching for functions basically + + #+BEGIN_SRC javascript + // Memoization for one argument function + var memoize = function (f) { + var cacheTable = {}; // Closure state, note that this hidden all throughout + + return function (n) { // Implemented for only one argument + var cachedValue = get(n, cacheTable); // Get cached value + + if (cachedValue === undefined) { // If there is no cached value + cachedValue = f.call(null, n); // Invoke the function + set(n, cachedValue, cacheTable); // Store the value + } + + return cachedValue; + } + }; + + var encrypt = memoize(function (text) { + var veryLongEncryptionAlgorithm = function _sha1Maybe(text) { /* ... */}; + + return veryLongEncryptionAlgorithm(text); + }); + #+END_SRC + + Again, this is just to show that closures are supposed to hide + state. + +** Starting With Functions + + There is a nice feature or trick in JavaScript known as *Immediately + Invoked Function Expression* or *IIFE* for short. + + This just creating a function and invoking immediately. + + #+BEGIN_SRC javascript + var y = (function _iife() { // Just create a function + return 1; // Why do this? + }()); // ... and wrap it with an open close parenthesis + + var z = 1; // Why not just do this instead? + #+END_SRC + + This is cute, but what is is good for? Since it is a function, it has + closure; so anything you put inside it, remains inside. + +*** Creating A Namespace + + When creating scripts for JavaScript, it's good to wrap the whole + script as an IIFE to prevent and variable leaks since the default + scope of variables is global. + + #+BEGIN_SRC javascript + (function _importMyCustomScript() { + var myScriptVar = 1; + + // Assume the global namespace for my app is XYZ + XYZ.myFunction = function () { return myScriptVar; }; + }()); + #+END_SRC + + This goes hand in hand with creating a global namespace if you are + working with the browser but we're not really going to dwell into that. + +*** Creating A Module + + You can also create a module using IIFE with this pattern. + + #+BEGIN_SRC javascript + // Using IIFE to make modules + var FP = (function () { + var hiddenVarInModule = 1; + + // Object.create allows us to create truly empty object + var Module = Object.create(null); // or {} is enough if you don't care + + // Object.freeze makes an object readonly + return Object.freeze(Module); // or Module if you don't care + // Return the module + }()); + #+END_SRC + +*** Starting An Idea + + Not the best use, but when you are trying to sketch a function and + can't decide if it can be refactored. Starting with IIFE can get you + started. + + #+BEGIN_SRC javascript + var originalFunction = function () { + // Blah blah blah -* At The End Of It All + // New feature, need a better name for this function + (function _newFeature() { // If this is used again, it is easier to refactor + // Everything I do is within this bubble + + var x = 1, + y= 'a'; + + // Do more stuff + }()) + + + // Blah blah blah + }; + #+END_SRC + + I usually sketch the prototype within an IIFE, and keep it there to + give it a good indication of what it does. Then if it used again, I + just turn into an legit function and refactor. + +** Stateful Talk + + Hiding state. + + This is what closures should ultimately do. But what if you had to? + Here are some examples. + +*** Objects As Stateful Value + + JavaScript has blessed syntax for object creation: easy and flexible + to use. + + #+BEGIN_SRC javascript + var myObject = { + myText: '', + myValue: null, + mySubObject: { + isEmpty: true + }, + }; + + // It's actuall okay to mutate + // NOTE: A comment to say that it mutates + var newObject = set('myText', 'xyz', myObject); + + console.log(myObject.myText); // out: 'xyz' + // but + console.log(newObject === myObject); // out: true + #+END_SRC + + I put an object in, I get an object out. I don't care if you mutated + it, but be very mindful or explicit about this. Comments are your + friends. Using objects as a data format is a solid way to go about it. + + One of your friends too in this field is known as *Immutability* which + might be a different topic. + +*** State As A Value + + What if given a big list of numbers, compute the average; what if + you could loop through it once? How would you do it? + + #+BEGIN_SRC javascript + var bigDoop = [1, 2, 3, /* ... */, 10000000]; // Just a very big set - Did the code look cleaner with these concepts? + // What appears to be more cumbersome + var computeAverage = function (numbers) { + var getSum = curry(get)(0), + getCount = curry(get)(1); - That is where it matters. + var totals = numbers.reduce(function (prev, number) { + var currentSum = getSum(prev), + currentCount = getCount(prev); -* JavaScript Is Functional + return [currentSum + number, currentCount + 1]; + }, [0, 0]); // Setting the initial state - End of the line. Wrap it up fool. I hope you found + var sum = get(0, totals), + count = get(1, totals); + + return sum / count; + }; + + console.log(computeAverage(bigDoop)); // out: More numbers than Iron Man + #+END_SRC + + Notice the use of a lists to pass in state, the count and the + sum. If you're in a pipeline, it is much better to make the state + explicit as a value for all to see and inspect. + +*** State As An Argument + + This is not really practical but just to get your attention. Let's + implement a fancy recursive factorial implementation. + + #+BEGIN_SRC javascript + // Avoid doing this, this is not helpful + var factorial = function (n) { + var factorialHelper = function _recurse(n, value) { + if (n <= 0) { // If done, return the value + return value; + } else { // Else, do it again with the new state + return _recurse(n - 1, value * n); // This is unhealthy to pass state + } + }; + + return factorialHelper(n, 1); // Call with initial state + }; + + var factorialWhile = function (n) { + var value = 1; + + while (n > 0) { + value *= n; + n-= 1; + } + + return value; + } + #+END_SRC + + If JavaScript was tail optimized, we would really do more of this + but it scares me to do it. But this is just to show the weird way to + pass in state. + +** Managing State Is Hard + + In reality, hiding state is hard. + + Being explicit about state almost translates to more code; this is + what can put you off to declare that global variable or just hack the + state variables. + + I suggest paying the explicit code than someone paying for it + implicitly. + +** Know Thy Closure + + Despite all that rant about state, if JavaScript didn't have closure, + I wouldn't know what to do about my work and there really wouldn't be + a functional paradigm for it. + + Imagine if you could access all the variables inside a function? + +** Quick Review + + Have I made my point? + + - Closures are underrated in managing state + - State makes code more complex, hide if you must + - Objects and lists make excellent function data interchange + +* Epilogue: JavaScript Is Functional + + End of the line. ** JavaScript Is Awesome @@ -1912,17 +3120,6 @@ (I could say the same thing about *Python* which holds a special place in my heart.) -** Reflection: Refactoring - - You could have noticed that all the machinery we built can be written - by hand, a simple function and a desire to make code cleaner. - - There are a lot of other things you can do with functions, really a - lot more I didn't mention or rather you can discover like uncharted - territory. - - *Use your imagination.* - ** The Power Of Functions In JavaScript If you forgot what I said, here it is. @@ -1932,15 +3129,13 @@ - Think input and output, processes not details - Avoid for loops and ifs - Hide implementation details + - Hide state as much as possible - *Have fun* ** What I Didn't Say Compare JavaScript with Haskell if you want to see the light - - Partial Application - - Closures - The chapter I removed from this presentation - Types - Monads - Functors @@ -1949,7 +3144,7 @@ ** Future Of Functional Programming - I am not an prophet but... + I am not an Oracle but... - Functional Reactive Programming - React & React Native