Skip to content

Instantly share code, notes, and snippets.

@emacdona
Last active November 15, 2018 17:29
Show Gist options
  • Save emacdona/313553ce2ce755443f2b36900bfac3a3 to your computer and use it in GitHub Desktop.
Save emacdona/313553ce2ce755443f2b36900bfac3a3 to your computer and use it in GitHub Desktop.
// 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