Notes and code snippets while studying Functional JavaScript by Michael Fogus, with the Asheville Hacker Book Club.
- Preface
- Chapter 1: Introducing Functional Programming
- Chapter 2: First-Class Functions and Applicative Programming
- Chapter 3: Variable Scope and Closures
- Chapter 4: Higher-Order Functions
- Chapter 5: Function-Building Functions
- avoid assigning variables more than once
- no eval
- no monkey patching
- favor functions over methods
[!] i often forget i can pass functions as parameters, without defining an anonymous function every time.
for example:
// i tend to reach for this:
[1, 2, 3].forEach(function(n) {
alert(n);
});
// and forget this:
[1, 2, 3].forEach(alert);
function note(message) {
console.log(message);
}
function prettyNumber(n) {
return 'Hello, I am the number ' + n;
}
// and this:
[1, 2, 3].map(prettyNumber)
.forEach(note);
//=> Hello, I am the number 1
//=> Hello, I am the number 2
//=> Hello, I am the number 3i rarely use apply in practice, took me a minute to deconstruct example. here's an example with three arguments.
function splat(fun) {
return function(array) {
return fun.apply(null, array);
}
}
var addThree = splat(function(a, b, c) { return a + b + c; });
addThree([5, 10, 15]);
console.log(sum);
//=> 30[!] i forgot that the first argument to
apply(andcall) is the value ofthisforfun. i also forgot about magicalargumentsobject. :)
[?] i'm sure it's a contrived example, but the "functional" rewrite of
parseAgerelying on global functionsfail,warnandnotefreaks me out. maybe this will be addressed in chapter 3 on closures for "data hiding".
in the comparator example, truthy and falsey are not defined. here is a complete example:
function exists(x) {
return x!= null;
}
function truthy(x) {
return (x !== false) && exists(x);
}
// comparator is a higher order function because
// it takes a function and returns a new function
function comparator(pred) {
return function(x, y) {
if (truthy(pred(x, y))) return -1;
else if (truthy(pred(y, x))) return 1;
else return 0;
}
}
function lessThan(x, y) {
return x < y;
}
function greaterThan(x, y) {
return x > y;
}
function isEqual(x, y) {
return x === y;
}
var resultAsc = [100, 1, 0, 10, -1, -2, -1].sort(comparator(lessThan));
console.log(resultAsc);
//=> [ -2, -1, -1, 0, 1, 10, 100 ]
var resultDesc = [100, 1, 0, 10, -1, -2, -1].sort(comparator(greaterThan));
console.log(resultDesc);
//=> [ 100, 10, 1, 0, -1, -1, -2 ]
var resultWat = [100, 1, 1, 0, 10, -1, -2, -1].sort(comparator(isEqual));
console.log(resultWat);
//=> [ 100, 1, 1, 0, 10, -1, -2, -1 ][!] why does passing
isEqualtocomparatorresult in no sorting? because, ironically, when two numbers are not equal, comparator will return 0 (since first two comparisons via isEqual return false), thereby signally equality to sort. when two numbers are equal, they will be swapped which is meaningless. hence no change.
in a footnote the author mentions that ECMAScript.next is discussing the possibility of support for classes.
this is now a fairly solidified spec for ES6 using the class keyword.
class Animal {
constructor(name) {
this.name = name;
this.say(name + ' has been born!');
}
say(message) {
console.log(message);
}
eat() {
this.say('eating right now ...');
}
}
class Cat extends Animal {
meow() {
this.say('meooow!');
}
}
var cat = new Cat('sebastian');
cat.eat();
cat.meow();
//=> sebastian has been born!
//=> eating right now ...
//=> meoow!at this point author seems to assume familiarity with underscore, using _.reduce, _.map and _.rest in example code without explanation. :/
in any case, tinkering with the implementation of lameCSV:
function lameCSV(str) {
var rows = str.split('\n');
return _.reduce(rows, function(table, row) {
var cols = row.split(',');
table.push(_.map(cols, function(c) {
return c.trim();
}));
return table;
}, []);
}
var frameworks = 'framework, language, age\n ' +
'rails, ruby, 10\n' +
'node.js, javascript, 5\n' +
'phoenix, elixir, 1';
var table = lameCSV(frameworks);
console.log(table);
//=> [ [ 'framework', 'language', 'age' ],
//=> [ 'rails', 'ruby', '10' ],
//=> [ 'node.js', 'javascript', '5' ],
//=> [ 'phoenix', 'elixir', '1' ] ]
var sorted = _.rest(table).sort();
console.log(sorted);
//=> [ [ 'node.js', 'javascript', '5' ],
//=> [ 'phoenix', 'elixir', '1' ],
//=> [ 'rails', 'ruby', '10' ] ][?] wait, how did javascript sort that last array of arrays?
well, according to Array.prototype.sort():
elements are sorted by converting them to strings and comparing
['node.js', 'javascript', '5'].toString();
//=> "node.js,javascript,5"[!] again, the idea of passing functions to functions, while basic to javascript, is still not the most intuitive approach for my brain.
for example, it took me a minute to unpack this:
function selectFrameworks(table) {
return _.rest(_.map(table, _.first));
}
console.log(selectFrameworks(table));
//=> [ 'rails', 'node.js', 'phoenix' ]intuitively, i may have written this function as:
function selectFrameworks(table) {
var firstCol = _.map(table, function(row) {
return _.first(row);
});
return _.rest(firstCol);
}in ruby collection methods are usually on the objects themselves, so it feels more intuitive:
# ruby
table.drop(1).map(&:first)vs.
// javascript
_.rest(_.map(table, _.first));[!] wow, this is so basic, but i rarely consider that core prototype functions are all accessible as strings in bracket notation, e.g.:
arr = [1, 2, 3];
arr['reverse'];
//=> function reverse() { [native code] }a functional programming language is [at minimum] one facilitating the the use and creation of first-class functions.
the term "first-class" means that something is just a value. a first-class function is one that can go anywhere that any other value can go.
var f = function() { return 42; }
var f = [function() { return 42; }];
var f = { fun: function() { return 42; }}
42 + (function() { return 42; })();
//=> 84a "higher-order" function can:
- take a function as an argument
- return a function as a result
// take a function as argument
function weirdAdd(n, f) {
return n + f();
}
weirdAdd(42, function() { return 42; });
//=> 84
// return a function as result
function weirdLogger() {
return function(message) {
console.log('weird!', message);
}
}
var logger = weirdLogger();
logger('is this thing on?');
//=> weird! is this thing on?- functional programming
- imperative programming
- prototype-based object-oriented programming
- metaprogramming
[!] chaining ftw!
Chaining You can use Underscore in either an object-oriented or a functional style, depending on your preference. The following two lines of code are identical ways to double a list of numbers.
_.map([1, 2, 3], function(n){ return n * 2; });
_([1, 2, 3]).map(function(n){ return n * 2; });chain.chain(obj)_ Returns a wrapped object. Calling methods on this object will continue to return wrapped objects until value is called.
my implemention of lyricSegment:
function pluralizer(singular, plural) {
return function(count) {
count = Math.abs(count);
return (count == 0 || count > 1) ? plural : singular;
}
}
function lyricSegment(n) {
var bottles = function(count) {
if (count > 0) {
return count + ' ' + pluralizer('bottle', 'bottles')(count);
} else {
return 'No more bottles';
}
}
return _.chain([])
.push(bottles(n) + ' of beer on the wall')
.push(bottles(n) + ' of beer')
.push('Take one down, pass it around')
.push(bottles(n - 1) + ' of beer on the wall')
.value();
}
console.log(lyricSegment(5));
//=> [ '5 bottles of beer on the wall',
//=> '5 bottles of beer',
//=> 'Take one down, pass it around',
//=> '4 bottles of beer on the wall' ]
console.log(lyricSegment(1));
//=> [ '1 bottle of beer on the wall',
//=> '1 bottle of beer',
//=> 'Take one down, pass it around',
//=> 'No more bottles of beer on the wall' ]functions vs methods: keep in mind that when I use the word "function" I mean a function that exists on its own and when I use "method" I mean a function created in the context of an object.
metaprogramming: programming occurs when you write code to do something and metaprogramming occurs when you write code that changes the way that something is interpreted.
applicative programming: is defined as the calling by
function bof afunction a, supplied as an argument tofunction boriginally.
collection-centric programming: functions built to operate on collections.
// function a
function double(n) { return n * 2; }
// function b
_.map([1, 2, 3], double);[?] why is this an examle of
reduceRight, it doesn't seem to be required?
function allOfReduceRight() {
if (_.isEmpty(arguments)) return true;
return _.reduceRight(arguments, function(truth, f) {
return truth && f();
}, true);
}
function allOfReduce() {
if (_.isEmpty(arguments)) return true;
return _.reduce(arguments, function(truth, f) {
return truth && f();
}, true);
}
function T() { return true; }
function F() { return false; }
console.log(allOfReduceRight());
console.log(allOfReduceRight(T, T));
console.log(allOfReduceRight(T, T, T, T, F));
//=> true
//=> true
//=> false
console.log(allOfReduce());
console.log(allOfReduce(T, T));
console.log(allOfReduce(T, T, T, T, F));
//=> true
//=> true
//=> falsehow about a lazy version of anyOf that uses find?
function anyOf() {
return h.exists(_.find(arguments, h.truthy));
}var target = {
helloName: function() {
return 'hello, ' + this.name;
},
sayHello: function(name) {
this.name = name;
console.log(this.helloName());
}
};
var wat = {
helloName: function() {
return 'wat';
}
}
target.sayHello.call(wat, 'sean');
//=> wat
_.bindAll(target, 'helloName', 'sayHello');
target.sayHello.call(wat, 'sean');
//=> hello, sean[!] _.bindAll ftw.
function curry2(fun) {
// actual function returned
return function(secondArg) {
// second function returned when first function, above, is called
return function(firstArg) {
// return application of both arguments to original function passed
return fun(firstArg, secondArg);
}
}
}
// ---- secondArg
// |
var div = function(num, divisor) { return num / divisor; }
// |
// ----firstArg
// --- secondArg (divisor)
// |
var div10 = curry2(div)(10);
// |
// ---- fun
// ------ firstArg (num)
// |
console.log(div10(50));
//=> 5Released under the MIT License by Sean Omlor.
