Iterator callbacks

Adapting signatures and preserving context in iterators.

JavaScript allows to pass functions around, just like any other object. In particular, functions can be passed as arguments to iterators. This makes loop iterations very succinct. In this example

1
2
3
errors.forEach(function printError(error) {
grunt.log.error(error);
});

function printError is unnecessary, one can pass grunt.log.error directly as argument to .forEach iterator

errors.forEach(grunt.log.error);

This is not the end of story, because grunt.log.error prints ALL its arguments, not just the first one.

Ignoring iterator arguments

Iterators often pass arguments that throw off our little logging functions, because they pass value, index and the entire array with each call. For example:

['foo', 'bar'].forEach(console.log);
// prints value, index and entire array with each call
foo 0 [ 'foo', 'bar' ]
bar 1 [ 'foo', 'bar' ]

If we are only interested in printing first value we could create a little functional adapter. I like to approach creating such utility functions by expressing the desired logic in plain English first, then "translating" the logic into JavaScript code.

unary is a function that takes a function A,
    and returns another function B that expects single argument.
    All B does:
        it call A with the given argument
        returns the result

Same logic in code

1
2
3
4
5
function unary(A) {
return function B(first) {
return A(first);
};
}

Use the adapter like this:

['foo', 'bar'].forEach(unary(console.log));
// prints
foo
bar

Bonus: for a hilarious example where things go wrong unless you adapt function signatures see The Madness of King JavaScript by Reginald Braithwaite. Then take a look at his library full of little functional goodies like unary, and download his excellent ebook JavaScript Allonge.

Another example is a shortcut to NodeJS path.join method, especially when mapping multiple paths relative to the current folder. For example,

1
2
3
4
var join = require('path').join;
var filenames = ['../foo', '../bar/baz'].map(function (relativeName) {
return join(__dirname, relativeName);
});

This can be expressed much shorter like this

1
2
3
var R = require('ramda');
var fromFolder = R.unary(R.partial(require('path').join, __dirname));
var filenames = ['../foo', '../bar/baz'].map(fromFolder);

Preserving scope

One has to be careful when passing functions that need their scope. For example, if you want to print count with each log statement, you might try this:

1
2
3
4
5
6
7
8
var logger = {
count: 0,
log: function (message) {
console.log(this.count, message);
this.count += 1;
}
};
['foo', 'bar'].forEach(logger.log);

but it generates weird output:

undefined 'foo'
NaN 'bar'

The problem is that when passing logger.log around, you separate the object logger from this inside the log function. JavaScript assumes this refers to the global object, unless told otherwise. First time the log is called, this.count is undefined in the global scope, then 1 is added to this undefined producing the NaN value from then on.

You can solve this problem by binding this reference to logger object before passing logger.log around:

['foo', 'bar'].forEach(logger.log.bind(logger));
// prints
0 'foo'
1 'bar'

If you intend to reuse the logger.log in several places, I advocate storing the .bind result as a variable

var lc = logger.log.bind(logger);
['foo', 'bar'].forEach(lc);