a → b
Input an a, get out a b
Any input a will map to one output b
a, x?, y?, ... → b? c? void?
... something happened inside some object ...
... some side effects happened, maybe affecting state somewhere else?
... and then something returns ... right?
... well, I get the same output for the same input then, right?
A function from Cat → Cat
A function from Cat → Cat null → Shit
When you build up a functional pipeline, everybody has to play nice.
capitalize :: String → String
What if there is no element with the ID you input?
document.getElementById.bind(document) // String → DOMElement
1
2
3
4
5
var capitalize = R.compose(
R.toUpperCase, // String → String
R.prop('textContent'), // Object → String
document.getElementById.bind(document) // String → DOMElement
);
n.b.: Same function from Cat → Cat
A functor is simply a function that lets you map over the contents of a container.
map :: (a → b) → Functor a → Functor b
n.b.: Same function from Cat → Cat, now null safe!
return this.value == null ? this : new Maybe(fn(this.value));
1
2
3
4
5
6
7
function Maybe(x) {
if (!(this instanceof Maybe)) { return new Maybe(x); }
this.value = x;
}
Maybe.prototype.map = function(fn) {
return this.value == null ? this : new Maybe(fn(this.value));
};
capitalize :: String → Maybe(String)
R.map(R.toUpperCase), // Maybe(String) → Maybe(String)
1
2
3
4
5
6
var capitalize = R.compose(
R.map(R.toUpperCase), // Maybe(String) → Maybe(String)
R.map(R.prop('textContent')), // Maybe(Object) → Maybe(String)
Maybe, // Object → Maybe(Object)
document.getElementById.bind(document) // String → DOMElement
);
right
) or a message (left
) return this.right == null ? this : new Either(this.left, f(this.right));
1
2
3
4
5
6
7
8
function Either(left, right) {
if (!(this instanceof Either)) { return new Either(left, right); }
this.left = left;
this.right = right;
}
Either.prototype.map = function(f) {
return this.right == null ? this : new Either(this.left, f(this.right));
}
map
operation is then
map(add1, Promise(2)); //=> Promise(3)
map :: (a → b) → [a] → [b]
(Arrays are complected with the notion of iteration.)
n.b.: Same function from Cat → Cat, yet again!
Applies a function (or functions) in a container to a value (or values) in a container.
ap :: [f] → [a] → [f a]
return concat(acc, map(fn, vs));
1
2
3
4
5
R.ap = curry(function(fns, vs) {
return foldl(function(acc, fn) {
return concat(acc, map(fn, vs));
}, [], fns);
});
return (typeof this.value !== 'function') ? new Maybe(null) : vs.map(this.value);
1
2
3
Maybe.prototype.ap = function(vs) {
return (typeof this.value !== 'function') ? new Maybe(null) : vs.map(this.value);
};
hasAbc :: [String] → Boolean
R.map(R.contains, [C]) //=> [Function: ([String] → Boolean)]
R.ap(R.map(R.contains, [C])) //=> [String] → [Boolean]
R.every(R.I) //=> [Boolean] → Boolean
Not quite right yet.... Is that the right signature for ap
?
R.ap(R.map(R.contains, [C])) // [String] → [Boolean]
1
2
3
4
var hasAbc = R.compose(
R.every(R.I), // [Boolean] → Boolean
R.ap(R.map(R.contains, [C])) // [String] → [Boolean]
);
Wraps an arbitrary object in a container.
of :: x → [x]
R.of = function(x) {
1
2
3
R.of = function(x) {
return [x];
};
What we want is a function hasAbc :: [String] → Boolean
R.of //=> * → [*]
R.ap(R.map(R.contains, [C])) //=> [[String]] → [Boolean]
R.every(R.I) //=> [Boolean] → Boolean
R.ap(R.map(R.contains, ['a', 'b', 'c'])), // [[String]] → [Boolean]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var hasAbc = R.compose(
R.every(R.I), // [Boolean] → Boolean
R.ap(R.map(R.contains, ['a', 'b', 'c'])), // [[String]] → [Boolean]
R.of // [String] → [[String]]
);
console.log('hasAbc(xyz)',
hasAbc(['x', 'y', 'z'])); // false
console.log('hasAbc(xyzab)',
hasAbc(['x', 'y', 'z', 'a', 'b'])); // false
console.log('hasAbc(cxbyaz)',
hasAbc(['c', 'x', 'b', 'y', 'a', 'z'])); // true
console.log('hasAbc(abcdefg)',
hasAbc(['a', 'b', 'c', 'd', 'e', 'f', 'g'])); // true
Illustrations courtesy of J. C. Phillipps (@jcphillipps)
No animals were harmed in the making of this presentation
1 / 81
#