Question

If you try to program in a functional style on JavaScript, you'll probably realize the conventional fn(obj,params...) form gets unreadable fast.

console.log(
    join(
        map(function(row){ return row.join(" "); },
            format(
                map(function(text){return align(text},
                    view),
                20))),
    "\n");

VS

view.map(function(text){return align(text)})
    .format(20)
    .map(function(row){return row.join(" ");})
    .join("\n")
    .log();

Problematically, the latter style could only be implemented extending the Object.prototype with new functions, which is globally regarded as evil. Yet, the readability is so superior that I'm prompted to just go ahead and do it anyway. Is there a better way to solve this problem?

Was it helpful?

Solution

Yes, there is a better way to solve the problem. Below is a demonstration using lodash.

Bad method:

import _ from "lodash";

_.chain([1, 2, 3])
  .map(x => [x, x*2])
  .flatten()
  .sort()
  .value();

Better method:

import map from "lodash/fp/map";
import flatten from "lodash/fp/flatten";
import sortBy from "lodash/fp/sortBy";
import flow from "lodash/fp/flow";

flow(
  map(x => [x, x*2]),
  flatten,
  sortBy(x => x) 
)([1,2,3]);

See Why using _.chain is a mistake. for a fantastic, in-depth explanation.

OTHER TIPS

A common "monadic" approach to this is to wrap an operand into your own object, call a chain of methods on that object and then extract the plain value back. Example from underscore.js docs:

var youngest = _.chain(stooges)
  .sortBy(function(stooge){ return stooge.age; })
  .map(function(stooge){ return stooge.name + ' is ' + stooge.age; })
  .first()
  .value();

The basic pattern is

  1. Create a constructor function, store the object's state as properties on this
  2. Add your chainable methods to the constructor's prototype. In the body of these functions, alter the object's state attached to this, then return this.
  3. Provide a method to use at the end of the chain to convert from your custom chainable object to a raw array or whatever the non-chainable analog is.

http://jsfiddle.net/XjRrn/ is a working example:

function ChainableArray(rawArray) {
     this._array = rawArray || [];
};
ChainableArray.prototype.map = function(fn) {
    this._array = _.map(this._array, fn);
    return this;
};
ChainableArray.prototype.incr = function(amount) {
    this._array = _.map(this._array, function (item) {return item + amount;});
    return this;
};
ChainableArray.prototype.toArray = function () {
    return _.clone(this._array);
};

The Query object from the mongoose.js library is a great case study to reference.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top