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 @@ + + +
+ +' + 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 @@+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. +
+ +Just a mediocre software developer.
@@ -1715,8 +1735,8 @@Judge a man by his questions rather than his answers @@ -1732,8 +1752,8 @@
Before We Begin
Where do I begin?
@@ -1748,17 +1768,17 @@+
But I am not here to bash on the language.
-+
How do we write good code in a (sucky but awesome) language?
How do write code that is…
@@ -1782,8 +1802,8 @@The building blocks of programs
@@ -1799,8 +1819,8 @@The best thing about JavaScript is its implementation of functions. It @@ -1819,32 +1839,33 @@
Functions In JavaScript
Just a brief review of the following.
arguments
keywordthis
and arguments
keywordcall
and apply
functionThe bread and butter of programmers
There are two primary ways to declare a function.
@@ -1860,12 +1881,17 @@There's three known ways to call or invoke a function.
@@ -1890,8 +1916,8 @@
return
is the output of the function. If there is no return
, an
implicit undefined
is the output.
@@ -1913,11 +1939,51 @@
+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. +
+ +-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.
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]
arguments
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' ++
arguments
An array-like object containing the arguments
@@ -1969,38 +2064,145 @@arguments
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 ++
-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)
.
+
+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; ++
+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
f.call()
and f.apply()
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 ++
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 ++
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' ++
-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.
-Inner functions have access to it's the variables of the outer function +Functions have access to variables to it's outer function.
-Good design is not about making grand plans, but about taking things -apart. +We'll talk about this more later
+
-– Rich Hickey +Not really going to talk about this, but it exists.
- --At last, let's talk about functions. -
+-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 -
+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] ++
+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 ++
+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 ++
+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. +
+ +++ ++Good design is not about making grand plans, but about taking things +apart. +
+ ++– Rich Hickey +
+
+At last, let's talk about functions. +
+ ++I don't want to bore you with a definition. It's really just a mindset +
+ +++ ++Christianity has not been tried and found wanting; it has been found +difficult and not tried. +
+ ++– Gilbert Chersterton +
+
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 is the most resilient parasite? Bacteria? A virus? An intestinal @@ -2156,8 +2630,8 @@
The Idea
The whole of the presentation
@@ -2165,29 +2639,23 @@map
, filter
and reduce
compose
and curry
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. -
-Let's talk about lists and their functions namely
@@ -2206,8 +2674,8 @@Who here uses a for loop like so?
@@ -2223,8 +2691,8 @@Intention
@@ -2255,8 +2723,8 @@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 @@
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 @@
Let's implement our own forEach
for analysis
-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 @@
The intention of the problem if we define it as a function.
@@ -2396,8 +2864,8 @@map
map
Thankfully the map method of lists allows this.
@@ -2412,11 +2880,10 @@map
map
Just some examples to get improve comprehension
@@ -2463,8 +2930,8 @@
Just like with forEach
, implenting the map
is easy.
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 @@
-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.
You can think of objects as a collection of key value pairs
@@ -2550,8 +3014,8 @@Pairs are just lists with two elements. Easy, right?
@@ -2569,8 +3033,8 @@Let's create a function to convert an object to a list of key value pairs @@ -2610,8 +3074,8 @@
Just a demonstration.
@@ -2643,8 +3107,8 @@Let's create a function to do the opposite: convert a function
@@ -2655,14 +3119,13 @@+
So what does it mean to think of objects as pairs?
What if you had an object and want to capitalize every string?
@@ -2722,8 +3185,8 @@mapObject
mapObject
Using all we made so far
@@ -2752,8 +3215,8 @@mapObject
mapObject
mapObject
What if we want to find the differences between an old and new object? @@ -2786,21 +3249,8 @@
mapObject
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? -
- -Let's move on to filtering collections given a criteria.
@@ -2831,33 +3281,8 @@
-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 --
filter
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 @@
filter
filter
filter
Again, we implement this for our own benefit.
@@ -2906,8 +3331,6 @@filter
filter
+
So we have another trick to use with lists. However, this trick has two other concepts attached to it.
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
Let's remove the first if
statement using the new found filter
power
We can chain the filter
to remove the other if
filter
filter
We just let the filter
chain be the result and we're done
filter
filter
filter
Notice how the code is much more readable and easier to understand without the explicit ifs? @@ -3180,8 +3608,8 @@
filter
If objects are also collections, then couldn't we filter over them as well? @@ -3220,8 +3648,8 @@
filterObject
filterObject
I can't believe this is too easy.
@@ -3243,8 +3671,8 @@filterObject
filterObject
filterObject
How about a contrived example?
@@ -3280,21 +3708,8 @@filterObject
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 -
- -By thinking about the operations over data, we can abstract the behavior to other containers. @@ -3317,8 +3732,8 @@
Finally, we move on to collecting or aggregating all the values of an collection.
@@ -3351,8 +3766,8 @@reduce
reduce
reduce
takes a combining function and a list and returns the
combined values of the list.
@@ -3382,11 +3797,10 @@
reduce
reduce
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
reduce
reduce
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
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 @@
reduce
Mean For Usreduce
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 @@
reduce
Mean For Usreduce
-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. -
- -Given a list of people, find all the people that are not minors and compute the total. @@ -3559,7 +3958,7 @@
Just a quick review of everything we've done.
@@ -3584,10 +3983,10 @@-Let's get to the fun stuff. +Let's get to the fun stuff
@@ -3601,21 +4000,21 @@
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
Let's say we have two buttons with their corresponding handlers.
@@ -3637,8 +4036,8 @@
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 @@
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 @@
doBefore
doBefore
Let's see how it makes the code much better
@@ -3715,7 +4114,7 @@doBefore
doBefore
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 @@
What if instead of executing before, we prevent the execution of the next one? @@ -3789,8 +4188,8 @@
How about we prevent a button being pressed in a short burst, could be useful for submit buttons @@ -3819,7 +4218,7 @@
Once you understand that you can join functions together, you can create a pipeline using functions. @@ -3871,8 +4270,8 @@
Just to demonstrate the extra parenthesis are not just dirty, but harder to read and maintain @@ -3905,8 +4304,8 @@
compose
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 @@
compose
compose
compose
compose
Let's join those two functions in with compose
compose
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.
compose
compose
Let's get acquainted with our new friend, make it your BFF if you can. @@ -4000,12 +4397,13 @@
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?
+compose
compose
So how do we compose functions all the way to the moon?
@@ -4037,8 +4435,8 @@compose
Let's implement a few things to improve the readability of our grand
composes
@@ -4075,8 +4473,8 @@
I prefer to read left to right instead of right to left with my function pipeline. @@ -4105,8 +4503,8 @@
composes
composes
With that out of the way, we simply use reduceRight
and pipe
in one master stroke
@@ -4124,13 +4522,12 @@
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
.
pipes
pipes
Just for the sake of symmetry.
@@ -4150,8 +4547,8 @@pipes
composes
composes
Let's check it out.
@@ -4180,8 +4577,8 @@composes
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 @@
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 @@
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 @@
Let's use composition to make the code cleaner.
@@ -4306,8 +4703,8 @@Although the example is quite contrived, the main point is to avoid complexity and maintain the simplicity of the process. @@ -4324,21 +4721,8 @@
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. -
- -Currying is a nice functional idea to sweeten composition
@@ -4373,8 +4757,8 @@Let's have another example with a function that takes three arguments
@@ -4404,8 +4788,8 @@But this seems to be a function behavior, not logic. Maybe we can separate this behavior. @@ -4440,8 +4824,8 @@
Copy pasting our trick from before
@@ -4469,8 +4853,8 @@curry
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 @@
curry
curry
curry
Just a contrived use of the generic curry
curry
Curry a function until it has one argument left which you can pass through a composition or mapping pipeline. @@ -4577,8 +4961,8 @@
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 @@
+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 love currying functions that all my functions in my module should be curried by default. @@ -4649,110 +5069,664 @@
curry
-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. +
-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-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
-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); ++
+The sorted list but there is something else afoot here. +
-console.log(fromPeanuts(characters)); // out: [{id:325,name:'Sally',source:'Peanuts'}] +console.log(games === games.sort()); ++
+This returns true. The list is sorted in place, so they're the +same. This is what is known as a side effect. +
+ ++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
-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.
+Functions should (almost) never do the following +
+ ++This is the hardest to follow. If you notice yourself making state or +mutating variables, consider the consequences.. +
+ +-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?
-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. +
+ ++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 ++
+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 +
+ ++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 ++
+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. +
+ ++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. +
+ ++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. +
+ ++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 +}()); ++
+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. +
+ ++Hiding state. +
+ ++This is what closures should ultimately do. But what if you had to? +Here are some examples. +
+ ++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. +
+ ++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. +
+ ++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. +
+ ++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. +
+ ++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? +
+ ++Have I made my point?
compose
, pipe
and curry
are fun tools to implement and have-End of the line. Wrap it up fool. +End of the line.
JavaScript is really cool for me because of the following.
@@ -4774,26 +5748,8 @@-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. -
- -If you forgot what I said, here it is.
@@ -4804,21 +5760,19 @@Compare JavaScript with Haskell if you want to see the light
-I am not an prophet but… +I am not an Oracle but…
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 @@
Thank you for letting me waste your time. I hope you learned something or even just the idea. @@ -4882,12 +5836,12 @@