Skip to content

hauntedhost/avlhbc-functional-javascript

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Screenshot

Summary

Notes and code snippets while studying Functional JavaScript by Michael Fogus, with the Asheville Hacker Book Club.

Contents:

Preface

code conventions:

  • avoid assigning variables more than once
  • no eval
  • no monkey patching
  • favor functions over methods

Chapter 1: Introducing Functional Javascript

the case for javascript:

passing functions

[!] 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 3

chapter-1/pass-the-func.js

apply/call

i 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

chapter-1/apply.js

[!] i forgot that the first argument to apply (and call) is the value of this for fun. i also forgot about magical arguments object. :)

functions as units of abstraction

[?] i'm sure it's a contrived example, but the "functional" rewrite of parseAge relying on global functions fail, warn and note freaks me out. maybe this will be addressed in chapter 3 on closures for "data hiding".

functions as units of behavior

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 ]

chapter-1/comparator.js

[!] why does passing isEqual to comparator result 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.

data as abstraction

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!

chapter-1/es6-class.html

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' ] ]

chapter-1/lame-csv.js

[?] 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] }

Chapter 2: First-Class Functions and Applicative Programming

functions as first-class things

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; })();
//=> 84

a "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?

chapter-2/weird-logger.js

javascript paradigms

  • 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' ]

chapter-2/99-bottles.js

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 b of a function a, supplied as an argument to function b originally.

collection-centric programming: functions built to operate on collections.

// function a
function double(n) { return n * 2; }

// function b
_.map([1, 2, 3], double);

chapter-2/double.js

reduceRight

[?] 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
//=> false

chapter-2/reduce-right.js

find

how about a lazy version of anyOf that uses find?

function anyOf() {
  return h.exists(_.find(arguments, h.truthy));
}

chapter-2/any-of.js

Chapter 3: Variable Scope and Closures

javascript's dynamic scope:

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

chapter-3/bind-all.js

[!] _.bindAll ftw.

Chapter 5: Function-Building Functions

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));
//=> 5

chapter-5/curry.js


License

Released under the MIT License by Sean Omlor.

About

Study notes for Functional JavaScript with the Asheville Hacker Book Club

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published