To see a working version of the code check out https://codesandbox.io/s/j497w26383 and open the console.
This file demonstrates a very basic List as an applicative functor for the purpose of demonstrating a simple list comprehension.
The idea for this was taken from: https://egghead.io/lessons/javascript-list-comprehensions-with-applicative-functors by https://twitter.com/drboolean
Here I'm implementing a simple List in order to try out the idea behind list comprehensions as outlined in the video.
This is not a production-use data structure. It's just me playing around to understand the internals better so that I feel comfortable using list comprehensions with a more sturdy data structure such as those provided in: https://github.com/DrBoolean/immutable-ext
We want to combine 3 lists such that for each entry on the left, it is combined with every entry on the right.
For example, given two lists: List(['teeshirt', 'sweater'])
and List(['small', 'medium', 'large'])
we want to produce a new list of: List(['teeshirt-small', 'teeshirt-medium', 'teeshirt-large', 'sweater-small', ...])
The traditional approach to this would be to use nested for-loops. Each time we have another List on the right, we would create a new inner loop. Let's see how this is achieved using list comprehensions.
// A little helper. We'll use this down at the bottom.
const liftA3 = (f, fx, fy, fz) =>
fx
.map(f)
.ap(fy)
.ap(fz);
// The List Applicative Functor
function List(x) {
this.x = x;
}
List.prototype.map = function(f) {
return new List(Array.isArray(this.x) ? this.x.map(f) : f(this.x));
};
// Flatten an array of List by a depth of 1
List.prototype.flatten = function() {
return Array.isArray(this.x)
? List.of(this.x.reduce((agg, next) => [...agg, ...next.x], []))
: this.x;
};
List.prototype.ap = function(other) {
return this.map(f => other.map(f)).flatten();
};
// Little helper so we can see what's inside our List
List.prototype.inspect = function() {
return `List([${this.x}])`;
};
List.of = x => new List(x);
List.of(x => y => z => `${x}-${y}-${z}`)
.ap(List.of(['teeshirt', 'sweater']))
.ap(List.of(['large', 'medium', 'small']))
.ap(List.of(['green', 'red', 'blue', 'pink']))
.inspect();
// or a simpler looking version using our liftA3
liftA3(
x => y => z => `${x}-${y}-${z}`,
List.of(["teeshirt", "sweater"]),
List.of(["large", "medium", "small"]),
List.of(["green", "red", "blue", "pink"])
).inspect();
To see a working version of the code check out https://codesandbox.io/s/j497w26383 and open the console.
The output of both our inspect()
calls is:
List([
teeshirt-large-green,
teeshirt-large-red,
teeshirt-large-blue,
teeshirt-large-pink,
teeshirt-medium-green,
teeshirt-medium-red,
teeshirt-medium-blue,
teeshirt-medium-pink,
teeshirt-small-green,
teeshirt-small-red,
teeshirt-small-blue,
teeshirt-small-pink,
sweater-large-green,
sweater-large-red,
sweater-large-blue,
sweater-large-pink,
sweater-medium-green,
sweater-medium-red,
sweater-medium-blue,
sweater-medium-pink,
sweater-small-green,
sweater-small-red,
sweater-small-blue,
sweater-small-pink
])