Last active
November 15, 2018 17:29
-
-
Save emacdona/313553ce2ce755443f2b36900bfac3a3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Run this file by typing this at the terminal while in the directory you saved this file to: | |
// (assuming you called the file 'main.js' | |
// node main.js | |
/********************************************************************************* | |
* | |
* PART I: Setup | |
* | |
*********************************************************************************/ | |
/* a function that sums two numbers */ | |
var sum = function( /* number */ a, /* number */ b ){ | |
return a + b; | |
} | |
/* | |
a function of two arguments (one of which is a function itself) that will build another function which takes a single argument and increments it by the number specified when you built it | |
*/ | |
var incrementBuilder = function( /* function that sums two numbers */ summer, /* size of increment for the incrementing function you are about to build */ increment ){ | |
// Warning: advanced topic to google at your leisure: It's worth noting that in the body of "incrementBuilder", I've created a "lexical closure" over the increment parameter | |
// ("lexical closure" is the term to google here) | |
// Summary: Even though in the code that follows I take the function returned here and assign it to a variable (multiple times, in fact), each instance I create "remembers" the | |
// context in which it was created. In particular, each instance remembers what the value of "increment" was when it was created. | |
// This is the function I'm building and then returning | |
return function( a ) { | |
return summer( a, increment ); | |
} | |
} | |
// Create three new functions by calling incrementBuilder to contruct them! | |
var plus1 = incrementBuilder( sum, 1 ); | |
var plus2 = incrementBuilder( sum, 2 ); | |
var plus10 = incrementBuilder( sum, 10 ); | |
/********************************************************************************* | |
* | |
* PART II: Using the functions I've built | |
* | |
*********************************************************************************/ | |
// plus1, plus2, and plus10 are three new functions of one argument that I built with the help of two other functions (sum and incrementBuilder) | |
console.log( "plus1(5): ", plus1(5) ); // Outputs: plus1(5): 6 | |
console.log( "plus2(10): ", plus2(10) ); // Outputs: plus2(10): 12 | |
console.log( "plus10(7): ", plus10(7) ); // Outputs: plus10(7): 17 | |
/********************************************************************************* | |
* | |
* PART III: Getting crazy | |
* | |
*********************************************************************************/ | |
// There's nothing special about "sum", by the way. But there is something special about incrementBuilder. It builds new functions with the help of | |
// other functions. | |
// To prove there's nothing special about sum, I'll use another function instead | |
var product = function( a, b ) { | |
return a * b; | |
} | |
// Now I'll use incrementBuilder to build a new type of function: one that multiplies by a given factor instead of increments | |
var times2 = incrementBuilder( product, 2 ); | |
console.log( "times2(10): ", times2(10) ); // Outputs: times2(10): 20 | |
// Warning: advanced topic: Clearly, I have choosen a bad name for "incrementBuilder" because it can do so much more than build just incrementing functions. The name I chose reflected the bias of my | |
// first example, when I had intended to pass it "sum" as its first argument. A better name for it would perhaps have been "curryFunctionOfTwoParamatersToFunctionOfOneParameter" -- Google: "currying" | |
// Also worth pointing out: I even chose a bad names for its first parameter, "summer" and its second parameter, "increment". Furthermore, not only does incrementBuilder create a "lexical closure" over | |
// "increment", it does so for all of the aguments passed to it, including "summer". | |
/********************************************************************************* | |
* | |
* PART IV: Getting crazier | |
* | |
*********************************************************************************/ | |
// It's also worth pointing out that you don't have to give functions a name. You can just instantiate them when they're needed. If you plan on using them a lot, though, definitely use a name. Otherwise, you'd have to define them | |
// every time you wanted to use them. | |
// Here's an example of passing an anonymous function as an argument to a function | |
var subtract5 = incrementBuilder( /* function without a name */ function( a, b ) { return a - b }, 5 ); | |
console.log( "subtract5(10): ", subtract5(10) ); // Outputs: subtract5(10): 5 | |
// But, you can also INVOKE an anymous function. So let's not even bother to give a name to the function we built, and just invoke it in-place. | |
console.log( | |
"Invoking anonymous function that is equivalent to subtract5(10): ", | |
incrementBuilder( function( a, b ) { return a - b }, 5 )(10) //Invoke the function we built, but haven't named, with parameter "10" | |
); // Outputs: Invoking anonymous function that is equivalent to subtract5(10): 5 | |
// Note: Anonymous functions are also known as "lambdas". I'm not sure that statement is 100% correct in a strict formal sense (maybe it is?) -- but to anyone who writes software, they are equivalent. | |
/********************************************************************************* | |
* | |
* PART V: Full generalization (very advanced topic). Just look it over and try | |
* to make sense of it. | |
* | |
*********************************************************************************/ | |
// Remember when I said I named things badly? Here is a generalized implementation of the curry function that will curry functions of M arguments to functions of N < M arguments. | |
// f: the function of M arguments to be currried | |
// outerArgs: the M-N arguments specified | |
// return value: function of N arguments | |
// Note: the "..." syntax is an ES6 thing. To make it work in older versions of JS, it's significantly uglier and will only hinder understanding of "curry" if I were to implement | |
// it that way. | |
var curry = function( f, ... outerArgs ) { | |
return function( ... innerArgs ) { | |
return f.apply(null, outerArgs.concat(innerArgs) ); | |
} | |
} | |
var sum5 = function (a, b, c, d, e){ | |
return a + b + c + d + e; | |
} | |
var sum2 = curry( sum5, 0, 0, 0 ); | |
console.log( "sum 5: ", sum5( 1, 1, 1, 1, 1 ) ); | |
console.log( "sum 2: ", sum2( 1, 1 ) ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment