Bootstart
Published in

Bootstart

Free your code from the shackles of `_.chain`.

Why using `_.chain` is a mistake.

import _ from "lodash";_.chain([1, 2, 3])
.map(x => [x, x*2])
.flatten()
.sort()
.value();
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]);

Prior Art

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

The Problem

It Can Get Big

_.chain = (array) => wrap(array, _); // Rough concept of chain
import _ from "lodash"; // Import everything._.chain([1,2,3]).map(x => x+1).value(); // Use the methods.
import chain from "lodash/chain";
import value from "lodash/value";
import map from "lodash/map";
import mixin from "lodash/mixin";
import _ from "lodash/wrapperLodash";
// Add the methods you want. The object generated by chain() will
// now have these methods.
mixin(_, {map: map, chain: chain, value: value});
_.chain([1,2,3]).map(x => x+1).value(); // Use the methods.

It’s Not Nice to Extend

import filter from "lodash/filter";const vowels = (array) => filter(array, str => /[aeiou]/i.test(str)
import _ from "lodash";_.mixin({vowels: vowels});_.chain(['ab','cd','ef']).vowels().value(); // Use the methods.
import chain from "lodash/chain";
import value from "lodash/value";
import map from "lodash/map";
import mixin from "lodash/mixin";
import _ from "lodash/wrapperLodash";
mixin(_, {
chain: chain,
value: value,
map: map, // Add existing lodash methods you need.
vowels: vowels, // Add your own lodash methods.
});
_.chain(['ab','cd','ef']).vowels().value(); // Use the methods.
import _ from "lodash";_.chain(['ab','cd','ef']).thru(vowels).value();
import chain from "lodash/chain";
import value from "lodash/value";
import mixin from "lodash/mixin";
import thru from "lodash/thru";
import _ from "lodash/wrapperLodash";
// Have one file that only imports core lodash methods you need.
mixin(_, {
chain: chain,
value: value,
thru: thru,
});
chain(['ab','cd','ef']).thru(vowels).value();

Much Ado About Something

The Solution

Currying

const add = (a, b) => a + b;
const add5 = _.partial(add, 5); // now `a` is bound (locked-in) to 5
const add5 = (b) => 5 + b; // this is what it looks like inside
add5(3); // equivalent to invoking add(5, 3);
const add = (a) => (b) => a + b;add(1)(2); // Repeated partial application.
const add5 = add(5); // Implicit partial application!

Composition

const add8 = (x) => add5(add3(x));
const add8 = compose(add5, add3);

All Together Now

import "map" from "lodash/map";map([1, 2, 3], (x) => x*2);
const double = map((x) => x*2); // We want this!double([1, 2, 3]); // It now operates on just the input array!
import map from "lodash/fp/map";map((x) => x*2)([1, 2, 3]); // We have this!
import _ from "lodash";_.chain([1, 2, 3])
.map(x => [x, x*2])
.flatten()
.sort()
.value();
import map from "lodash/fp/map";
import flatten from "lodash/fp/flatten";
import sortBy from "lodash/fp/sortBy";
import compose from "lodash/fp/compose";
compose(
sortBy(x => x),
flatten,
map(x => [x, x*2])
)([1,2,3]);

Caveats

Argument Ordering

compose(
sortBy(x => x),
flatten,
map(x => [x, x*2])
);
sortBy(x => x)(
flatten(
map(x => [x, x*2])
)
)([1, 2, 3]);
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]);

No Wrapper Object

“In fact our composition with flow/flowRight uses chaining internally (when monolithic) to support shortcut fusion in composed methods”

Careful with Currying

_.chain([1, 6, 2]).sortBy().value();
flow(sortBy)([1, 6, 2]);
const sortBy = (sorter = _.identity) => (array) => ...
sortBy([1, 6, 2]);
sortBy(/* _.identity */)([1, 6, 2]);
flow(sortBy())([1, 6, 2]);

Further Exploration

add :: Num -> Num -> Num
add = x y = x + y
add 2 4
//          Num -> Num -> Num
const add = (a) => (b) => a + b;
add(2)(4)
add8 = add5 . add3
//                   add5 . add3
const add8 = compose(add5, add3);
[1, 2, 3]
|> map(x => [x, x*2])
|> flatten,
|> sortBy(x => x);

What does it all mean?

 by the author.

--

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Izaak Schroeder

Izaak Schroeder

Things.

More from Medium

TypeScript Enums: What they are and why you should avoid them

The Iterator Pattern

The case against Non Null Assertion Operator !

Eight Queens Puzzle in TypeScript’s Type System

A chess piece with a golden crown on its top