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
+
+
+
+
+
+
+
+
+
+
+
+
+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.
+
+
+
+
+
+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.
+
+
+
+
+
+
+
+
+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
-
+
-
-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