Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
A Million Ways to
Fold in JS
Millionways
Agenda
[Recursion, Corecursion, Transducers, Monoids, F-Algebras]
Millionways
for (i = 0; i < xs.length; i++) {
xs[i] += 1;
}
Millionways
Recursion
var sum = function(xs) {
if(xs.length === 0) return 0;
return first(xs) + sum(rest(xs));
}
var sum = function(xs) {
if(xs.length === 0) return 0;
return first(xs) + sum(rest(xs));
}
Base Case
Action
Recursion
var reverse = function(xs) {
if(xs.length === 0) return [];
return reverse(rest(xs)).concat(first(xs));
}
var reverse = function(xs) {
if(xs.length === 0) return [];
return reverse(rest(xs)).concat(first(xs));
}
Base Case
Action
Recursion
var sum = function(xs) {
if(xs.length === 0) return 0;
return first(xs) + sum(rest(xs));
}
sum([1,2,3])
// 1 + sum([2,3])
// 1 + (2 + sum([3]))
// 1 + (2 + (3 + sum([])))
// 1 + (2 + (3 + 0))
// 1 + (2 + 3)
// 1 + 5
//=> 6
Millionways
ES6
ES6
ES6!
Err, i mean 2015
Tail Recursive
var sum = function(xs) {
if(xs.length === 0) return 0;
return first(xs) + sum(rest(xs));
}
var sum = function(xs) {
if(xs.length === 0) return 0;
return first(xs) + sum(rest(xs));
}
var sum = function(xs) {
if(xs.length === 0) return 0;
return first(xs) + sum(rest(xs));
}
RangeError: Maximum call stack size exceeded
var sum = function(xs) {
if(xs.length === 0) return 0;
return first(xs) + sum(rest(xs));
}
var sum = function(list) {
function go(acc, xs) {
if(xs.length === 0) return acc;
return go(acc+first(xs), rest(xs));
}
return go(0, list)
}
var sum = function(xs) {
if(xs.length === 0) return 0;
return first(xs) + sum(rest(xs));
}
var sum = function(list) {
function go(acc, xs) {
if(xs.length === 0) return acc;
return go(acc+first(xs), rest(xs));
}
return go(0, list)
}
var reduce = function(f, acc, xs) {
if(xs.length === 0) return acc;
return reduce(f, f(acc, first(xs)), rest(xs));
}
var reduce = function(f, acc, xs) {
if(xs.length === 0) return acc;
return reduce(f, f(acc, first(xs)), rest(xs));
}
var sum = function(xs){
return reduce((acc, x) => x + acc, 0, xs)
}
var reverse = function(xs) {
return reduce((acc, x) => [x].concat(acc), [], xs)
}
var map = function(f, xs) {
return reduce((acc, x) => acc.concat(f(x)), [], xs)
}
var filter = function(f, xs) {
return reduce((acc, x) => f(x) ? acc.concat(x) : acc, [], xs)
}
Catamorphism
+
Millionways
any
all
size
max
min
sortBy
find
groupBy
first
last
take
drop
etc…
Any loop can be captured with a fold!
Apomorphism
Paramorphism
var para = function(f, acc, xs) {
if(xs.length === 0) return acc;
return para(f, f(acc, first(xs), xs), rest(xs));
}
var para = function(f, acc, xs) {
if(xs.length === 0) return acc;
return para(f, f(acc, first(xs), xs), rest(xs));
}
HORS
HORS
Agenda
[Corecursion, Transducers, Monoids, F-Algebras]
Recursion
Anamorphism
var unfold = function(f, seed) {
function go(f, seed, acc) {
var res = f(seed);
return res ? go(f, res[1], acc.concat([res[0]])) : acc;
}
return go(f, seed, [])
}
unfold(x => if(x < 26) [String.fromCharCode(x+65), x+1], 0);
//=> [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z]
var range = function(i, count) {
return unfold(x => if(x <= count) [x, x+1], i);
}
range(5,10)
//=> [ 5, 6, 7, 8, 9, 10 ]
var tree = Node(Node(Leaf(2), 1, Leaf(3)), 0, Leaf(4))
fold((acc, x) => acc + x, 0, tree)
//=> 10
var clicks = $("#red-button").asEventStream("click")
fold((acc, el) => acc.append(el.id), $("#clicklog"), clicks)
//=> <div id=“clicklog”/>
var us = {
“391": "keith@suburban",
“52": “kaseem@funny.net"
}
fold((acc, addr) => acc.cc(addr), Email.create(), us)
//=> Email
Millionways
Cons(3, Cons(4, Cons(5, Nil)));
tail3 tail4 tail5 Nil
Linked List
var Nil = {}
var _Cons = function(h, tl) {
this.head = h;
this.tail = tl;
};
var Cons = function(h, tl) { return new _Cons(h, tl) }
var lst = Cons(3, Cons(4, Cons(5, Nil)));
1
Node(Node(Leaf(2), 1, Leaf(3)), 0, Leaf(4))
2 3
64
5
7
Tree
var Empty = {}
var _Leaf = function(x) { this.x = x; }
var Leaf = function(x) { return new _Leaf(x) }
var _Node = function(l, x, r) {
this.left = l;
this.x = x;
this.right = r;
}
var Node = function(l, x, r) { return new _Node(l, x, r) }
Agenda
[Transducers, Monoids, F-Algebras]
Recursion
Corecursion
var map = function(f, xs) {
return reduce((acc, x) => concat(acc, f(x)), [], xs)
}
var map = function(f, xs) {
return reduce((acc, x) => concat(acc, f(x)), [], xs)
}
Accumulation
Transformation Iteration
var map = function(f, xs) {
return reduce((acc, x) => concat(acc, f(x)), [], xs)
}
var mapper = function(f) {
return (acc, x) => concat(acc, f(x))
}
reduce(mapper(x => x + 1), [], [1,2,3])
//=> [2,3,4]
var mapper = function(f) {
return (acc, x) => concat(acc, f(x))
}
reduce(mapper(x => x + 1), [], [1,2,3])
//=> [2,3,4]
var mapper = function(f, cnct) {
return (acc, x) => cnct(acc, f(x))
}
reduce(mapper(x => x + 1, concat), [], [1,2,3])
//=> [2,3,4]
var mapper = function(f, cnct) {
return (acc, x) => cnct(acc, f(x))
}
reduce(mapper(x => x + 1, concat), [], [1,2,3])
//=> [2,3,4]
var filterer = function(f, cnct) {
return (acc, x) => f(x) ? cnct(acc, x) : acc
}
reduce(filterer(x => x > 1, concat), [], [1,2,3])
//=> [2,3]
var filterer = function(f, cnct) {
return (acc, x) => f(x) ? cnct(acc, x) : acc
}
reduce(filterer(x => x > 1, concat), [], [1,2,3])
//=> [2,3]
var filterer = function(f, cnct) {
return (acc, x) => f(x) ? cnct(acc, x) : acc
}
reduce(filterer(x => x > 1, concat), [], [1,2,3])
//=> [2,3]
var copy = function(xs) {
return reduce(concat, [], xs)
}
filterer(x => x > 1, mapper(x => x + 1, concat))
//=> (acc, x) => x > 1 ? concat(acc, f(x)) : acc
Millionways
filterer(x => x > 1, mapper(x => x + 1, concat))
//=> (acc, x) => x > 1 ? concat(acc, f(x)) : acc
reduce(filterer(x => x > 1,
mapper(x => x + 1, concat)),
[], [1,2,3])
//=> [3,4]
reduce(filterer(x => x > 1,
mapper(x => x + 1, concat)),
[], [1,2,3])
//=> [3,4]
reduce(filterer(x => x > 1,
mapper(x => x + 1, concat)),
[], [1,2,3])
//=> [3,4]
reduce(filterer(x => x > 1,
mapper(x => x + 1, append)),
Nil, Cons(1, Cons(2, Cons(3, Nil))))
//=> [3,4]
reduce(filterer(x => x > 1,
mapper(x => x + 1, append)),
Nil, Cons(1, Cons(2, Cons(3, Nil))))
//=> [3,4]
reduce(filterer(x => x > 1,
mapper(x => x + 1, insert)),
Empty, Node(Node(Leaf(2), 1, Leaf(3)), 0, Leaf(4)))
//=> [3,4]
reduce(filterer(x => x > 1,
mapper(x => x + 1, insert)),
Empty, Node(Node(Leaf(2), 1, Leaf(3)), 0, Leaf(4)))
//=> [3,4]
Iteration
Transformation
Accumulation
Agenda
[Monoids, F-Algebras]
Recursion
Corecursion
Transducers
// left identity
concat([], xs) == xs
// right identity
concat(xs, []) == xs
// associativity
concat(concat(xs, ys), zs) == concat(xs, concat(ys, zs))
Millionways
var sum = function(xs) {
return reduce((acc, x) => acc + x, 0, xs)
}
var _Sum = function(x) { this.val = x }
var Sum = function(x){ return new _Sum(x) }
_Sum.prototype.concat = function(y){
return Sum(this.val + y.val)
}
_Sum.prototype.empty = function(){ return Sum(0) }
var sum = function(xs) {
return reduce((acc, x) => acc + x, 0, xs)
}
_Sum.prototype.concat = function(y){
return Sum(this.val + y.val)
}
_Sum.prototype.empty = function(){ return Sum(0) }
var sum = function(xs) {
return reduce((acc, x) => acc + x, 0, xs)
}
fold([Sum(1), Sum(2), Sum(3), Sum(4)])
//=> Sum(10)
_Sum.prototype.concat = function(y){
return Sum(this.val + y.val)
}
_Sum.prototype.empty = function(){ return Sum(0) }
fold([Product(1),Product(2),Product(3),Product(4)])
//=> Product(24)
_Product.prototype.concat = function(y){
return Product(this.val * y.val)
}
_Product.prototype.empty = function(){ return Product(1) }
fold([Max(11),Max(16),Max(3),Max(9)])
//=> Max(16)
_Max.prototype.concat = function(y){
return Max(this.val > y.val ? this.val : y.val)
}
_Max.prototype.empty = function(){ return Max(-Infinity) }
_All.prototype.concat = function(y){
return Any(this.val && y.val)
}
_All.prototype.empty = function(){ return All(true) }
fold([All(false), All(false), All(true), All(false)])
//=> All(false)
_Any.prototype.concat = function(y){
return Any(this.val || y.val)
}
_Any.prototype.empty = function(){ return Any(false) }
fold([Any(false), Any(false), Any(true), Any(false)])
//=> Any(true)
any
all
max
min
and
or
product
sum
find
contains
concat
toList
_Any.prototype.concat = function(y){
return Any(this.val || y.val)
}
_Any.prototype.empty = function(){ return Any(false) }
fold([Any(false), Any(false), Any(true), Any(false)])
//=> Any(true)
var foldMap = function(f,xs) {
return compose(fold, map(f))(xs);
}
var sum = function(xs){ return foldMap(Sum, xs).val }
var max = function(xs){ return foldMap(Max, xs).val }
var any = function(xs){ return foldMap(Any, xs).val }
var tree = Node(Node(Leaf(2), 1, Leaf(3)), 2, Leaf(4))
sum(tree)
//=> 12
product(tree)
//=> 38
max(tree)
//=> 4
var lst = Cons(true, Cons(false, Cons(true, Nil)))
any(lst)
//=> true
all(lst)
//=> false
Millionways
Iteration
Transformation
Accumulation
[Sum(a)] -> Sum(a)
[Product(a)] -> Product(a)
[Max(a)] -> Max(a)
F(a) -> a
Agenda
[F-Algebras]
Recursion
Corecursion
Transducers
Monoids
No, not
That F
var cata = function(f, xs) {
return f(xs.map(ys => cata(f,ys)))
}
var cata = function(f, xs) {
return f(xs.map(ys => cata(f,ys)))
}
var cata = function(f, xs) {
return f(xs.map(ys => cata(f,ys)))
}
Fixed point of a Functor
Nil.map = function(f) { return Nil; }
_Cons.prototype.map = function(f) {
return Cons(this.head, f(this.tail))
}
Millionways
var sum = function(x) {
return (x === Nil) ? 0 : x.head + x.tail
}
var lst = Cons(2, Cons(3, Cons(4, Nil)));
cata(sum, lst);
//=> 9
var sum = function(x) {
return (x === Nil) ? 0 : x.head + x.tail
}
var lst = Cons(2, Cons(3, Cons(4, Nil)));
cata(sum, lst);
//=> 9
Algebra
var sum = function(x) {
return (x === Nil) ? 0 : x.head + x.tail
}
var lst = Cons(2, Cons(3, Cons(4, Nil)));
cata(sum, lst);
// Nil
// Cons(4, 0)
// Cons(3, 4)
// Cons(2, 7)
//=> 9
map(x => x + 1, Cons(2, Cons(3, Cons(4, Nil))))
//=> Cons(3, Cons(4, Cons(5, Nil)))
var map = function(f, xs) {
return cata(x => (x == Nil) ? Nil : Cons(f(x.head), x.tail), xs)
}
Empty.map = function(f) { return Empty }
_Leaf.prototype.map = function(f) {
return Leaf(this.x)
}
_Node.prototype.map = function(f) {
return Node(f(this.left), this.x, f(this.right))
}
cata(t =>
switch (t.constructor) {
case _Node: return t.left + t.x + t.right;
case _Leaf: return t.x;
default: 0;
}, tr)
//=> 10
var tr = Node(Node(Leaf(2), 1, Leaf(3)), 0, Leaf(4))
var ana = function(g, a) {
return g(a).map(x => ana(g,x))
}
var ana = function(g, a) {
return g(a).map(x => ana(g,x))
}
var arrToList = function(xs) {
return xs.length === 0 ? Nil : Cons(first(xs), rest(xs))
}
ana(arrToList, [1,2,3,4,5])
//=> Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Nil)))))
CoAlgebra
var arrToList = function(xs) {
return xs.length === 0 ? Nil : Cons(first(xs), rest(xs))
}
ana(arrToList, [1,2,3,4,5])
//=> Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Nil)))))
Co-Scary
var arrToList = function(xs) {
return xs.length === 0 ? Nil : Cons(first(xs), rest(xs))
}
ana(arrToList, [1,2,3,4,5])
//=> Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Nil)))))
var makeAlphabet = function(x) {
if(x > 25) return Nil;
return Cons(String.fromCharCode(x+65), x+1);
}
ana(makeAlphabet, 0)
//=> Cons(A, Cons(B, Cons(C, Cons(D, Cons(E, Cons(F, Cons(G, Cons(H
var range = function(acc, count) {
return ana(x => (x >= count) ? Nil : Cons(x, x+1), acc)
}
range(2, 10)
//=> Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Cons(7, Cons(8,
Cons(9, Nil))))))))
Iteration
Transformation
Accumulation
Millionways
Mul(Add(Const(2), Const(3)), Const(4))
Millionways
Mul(Add(Const(2), Const(3)), Const(4))
var _Const = function(val) { this.val = val }
var Const = function(x) { return new _Const(x) }
var _Add = function(x, y) {
this.x = x;
this.y = y;
}
var Add = function(x, y) { return new _Add(x, y) }
var _Mul = function(x, y) {
this.x = x
this.y = y
}
var Mul = function(x, y) { return new _Mul(x, y) }
_Const.prototype.map = function(f) { return this }
_Add.prototype.map = function(f) {
return Add(f(this.x), f(this.y))
}
_Mul.prototype.map = function(f) {
return Mul(f(this.x), f(this.y))
}
var interpret = function(a) {
switch(a.constructor) {
case _Mul: return a.x * a.y;
case _Add: return a.x + a.y;
case _Const: return a.val;
}
}
var program = Mul(Add(Const(2), Const(3)), Const(4))
cata(interpret, program);
//=> 20
var _Concat = function(v, next) {
this.val = v;
this.next = next;
}
var Concat = function(v, x){ return new _Concat(v, x) }
var _Replace = function(v, x, next) {
this.val = v;
this.x = x;
this.next = next;
}
var Replace = function(v, x, nt){ return new _Replace(v, x, nt) }
var _Input = function(v) { this.val = v }
var Input = function(v){ return new _Input(v) }
_Concat.prototype.map = function(f) {
return Concat(this.val, f(this.next))
}
_Replace.prototype.map = function(f) {
return Replace(this.val, this.x, f(this.next))
}
_Input.prototype.map = function(f) {
return Input(this.val)
}
var interpret = function(t) {
switch (t.constructor) {
case _Concat: return t.next.concat(t.val);
case _Replace: return t.next.replace(t.val, t.x);
case _Input: return t.val;
}
}
var prog = Concat("world", Replace("h", "m", Input(“hello")))
cata(interpret, prog)
//=> melloworld
var interpret1 = function(t) {
switch (t.constructor) {
case _Concat:
return "concatting “+t.val+" after "+t.next;
case _Replace:
return "replacing "+t.val+" with "+t.x+" on "+t.next;
case _Input:
return t.val;
}
}
var prog = Concat("world", Replace("h", "m", Input(“hello")))
cata(interpret1, prog)
//=> concatting world after replacing h with m on hello
Agenda
Recursion
Corecursion
Transducers
Monoids
F-Alegbras
Millionways
Millionways
THanks!
@drboolean
https://github.com/DrBoolean/RecursionTalk

More Related Content

Millionways

  • 1. A Million Ways to Fold in JS
  • 5. for (i = 0; i < xs.length; i++) { xs[i] += 1; }
  • 8. var sum = function(xs) { if(xs.length === 0) return 0; return first(xs) + sum(rest(xs)); }
  • 9. var sum = function(xs) { if(xs.length === 0) return 0; return first(xs) + sum(rest(xs)); } Base Case Action Recursion
  • 10. var reverse = function(xs) { if(xs.length === 0) return []; return reverse(rest(xs)).concat(first(xs)); }
  • 11. var reverse = function(xs) { if(xs.length === 0) return []; return reverse(rest(xs)).concat(first(xs)); } Base Case Action Recursion
  • 12. var sum = function(xs) { if(xs.length === 0) return 0; return first(xs) + sum(rest(xs)); } sum([1,2,3]) // 1 + sum([2,3]) // 1 + (2 + sum([3])) // 1 + (2 + (3 + sum([]))) // 1 + (2 + (3 + 0)) // 1 + (2 + 3) // 1 + 5 //=> 6
  • 14. ES6
  • 15. ES6
  • 18. var sum = function(xs) { if(xs.length === 0) return 0; return first(xs) + sum(rest(xs)); }
  • 19. var sum = function(xs) { if(xs.length === 0) return 0; return first(xs) + sum(rest(xs)); }
  • 20. var sum = function(xs) { if(xs.length === 0) return 0; return first(xs) + sum(rest(xs)); } RangeError: Maximum call stack size exceeded
  • 21. var sum = function(xs) { if(xs.length === 0) return 0; return first(xs) + sum(rest(xs)); } var sum = function(list) { function go(acc, xs) { if(xs.length === 0) return acc; return go(acc+first(xs), rest(xs)); } return go(0, list) }
  • 22. var sum = function(xs) { if(xs.length === 0) return 0; return first(xs) + sum(rest(xs)); } var sum = function(list) { function go(acc, xs) { if(xs.length === 0) return acc; return go(acc+first(xs), rest(xs)); } return go(0, list) }
  • 23. var reduce = function(f, acc, xs) { if(xs.length === 0) return acc; return reduce(f, f(acc, first(xs)), rest(xs)); }
  • 24. var reduce = function(f, acc, xs) { if(xs.length === 0) return acc; return reduce(f, f(acc, first(xs)), rest(xs)); }
  • 25. var sum = function(xs){ return reduce((acc, x) => x + acc, 0, xs) } var reverse = function(xs) { return reduce((acc, x) => [x].concat(acc), [], xs) }
  • 26. var map = function(f, xs) { return reduce((acc, x) => acc.concat(f(x)), [], xs) } var filter = function(f, xs) { return reduce((acc, x) => f(x) ? acc.concat(x) : acc, [], xs) }
  • 30. Any loop can be captured with a fold!
  • 33. var para = function(f, acc, xs) { if(xs.length === 0) return acc; return para(f, f(acc, first(xs), xs), rest(xs)); }
  • 34. var para = function(f, acc, xs) { if(xs.length === 0) return acc; return para(f, f(acc, first(xs), xs), rest(xs)); }
  • 35. HORS
  • 36. HORS
  • 39. var unfold = function(f, seed) { function go(f, seed, acc) { var res = f(seed); return res ? go(f, res[1], acc.concat([res[0]])) : acc; } return go(f, seed, []) }
  • 40. unfold(x => if(x < 26) [String.fromCharCode(x+65), x+1], 0); //=> [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z]
  • 41. var range = function(i, count) { return unfold(x => if(x <= count) [x, x+1], i); } range(5,10) //=> [ 5, 6, 7, 8, 9, 10 ]
  • 42. var tree = Node(Node(Leaf(2), 1, Leaf(3)), 0, Leaf(4)) fold((acc, x) => acc + x, 0, tree) //=> 10 var clicks = $("#red-button").asEventStream("click") fold((acc, el) => acc.append(el.id), $("#clicklog"), clicks) //=> <div id=“clicklog”/> var us = { “391": "keith@suburban", “52": “kaseem@funny.net" } fold((acc, addr) => acc.cc(addr), Email.create(), us) //=> Email
  • 44. Cons(3, Cons(4, Cons(5, Nil))); tail3 tail4 tail5 Nil Linked List
  • 45. var Nil = {} var _Cons = function(h, tl) { this.head = h; this.tail = tl; }; var Cons = function(h, tl) { return new _Cons(h, tl) } var lst = Cons(3, Cons(4, Cons(5, Nil)));
  • 46. 1 Node(Node(Leaf(2), 1, Leaf(3)), 0, Leaf(4)) 2 3 64 5 7 Tree
  • 47. var Empty = {} var _Leaf = function(x) { this.x = x; } var Leaf = function(x) { return new _Leaf(x) } var _Node = function(l, x, r) { this.left = l; this.x = x; this.right = r; } var Node = function(l, x, r) { return new _Node(l, x, r) }
  • 49. var map = function(f, xs) { return reduce((acc, x) => concat(acc, f(x)), [], xs) }
  • 50. var map = function(f, xs) { return reduce((acc, x) => concat(acc, f(x)), [], xs) } Accumulation Transformation Iteration
  • 51. var map = function(f, xs) { return reduce((acc, x) => concat(acc, f(x)), [], xs) }
  • 52. var mapper = function(f) { return (acc, x) => concat(acc, f(x)) } reduce(mapper(x => x + 1), [], [1,2,3]) //=> [2,3,4]
  • 53. var mapper = function(f) { return (acc, x) => concat(acc, f(x)) } reduce(mapper(x => x + 1), [], [1,2,3]) //=> [2,3,4]
  • 54. var mapper = function(f, cnct) { return (acc, x) => cnct(acc, f(x)) } reduce(mapper(x => x + 1, concat), [], [1,2,3]) //=> [2,3,4]
  • 55. var mapper = function(f, cnct) { return (acc, x) => cnct(acc, f(x)) } reduce(mapper(x => x + 1, concat), [], [1,2,3]) //=> [2,3,4]
  • 56. var filterer = function(f, cnct) { return (acc, x) => f(x) ? cnct(acc, x) : acc } reduce(filterer(x => x > 1, concat), [], [1,2,3]) //=> [2,3]
  • 57. var filterer = function(f, cnct) { return (acc, x) => f(x) ? cnct(acc, x) : acc } reduce(filterer(x => x > 1, concat), [], [1,2,3]) //=> [2,3]
  • 58. var filterer = function(f, cnct) { return (acc, x) => f(x) ? cnct(acc, x) : acc } reduce(filterer(x => x > 1, concat), [], [1,2,3]) //=> [2,3]
  • 59. var copy = function(xs) { return reduce(concat, [], xs) }
  • 60. filterer(x => x > 1, mapper(x => x + 1, concat)) //=> (acc, x) => x > 1 ? concat(acc, f(x)) : acc
  • 62. filterer(x => x > 1, mapper(x => x + 1, concat)) //=> (acc, x) => x > 1 ? concat(acc, f(x)) : acc
  • 63. reduce(filterer(x => x > 1, mapper(x => x + 1, concat)), [], [1,2,3]) //=> [3,4]
  • 64. reduce(filterer(x => x > 1, mapper(x => x + 1, concat)), [], [1,2,3]) //=> [3,4]
  • 65. reduce(filterer(x => x > 1, mapper(x => x + 1, concat)), [], [1,2,3]) //=> [3,4]
  • 66. reduce(filterer(x => x > 1, mapper(x => x + 1, append)), Nil, Cons(1, Cons(2, Cons(3, Nil)))) //=> [3,4]
  • 67. reduce(filterer(x => x > 1, mapper(x => x + 1, append)), Nil, Cons(1, Cons(2, Cons(3, Nil)))) //=> [3,4]
  • 68. reduce(filterer(x => x > 1, mapper(x => x + 1, insert)), Empty, Node(Node(Leaf(2), 1, Leaf(3)), 0, Leaf(4))) //=> [3,4]
  • 69. reduce(filterer(x => x > 1, mapper(x => x + 1, insert)), Empty, Node(Node(Leaf(2), 1, Leaf(3)), 0, Leaf(4))) //=> [3,4]
  • 72. // left identity concat([], xs) == xs // right identity concat(xs, []) == xs // associativity concat(concat(xs, ys), zs) == concat(xs, concat(ys, zs))
  • 74. var sum = function(xs) { return reduce((acc, x) => acc + x, 0, xs) }
  • 75. var _Sum = function(x) { this.val = x } var Sum = function(x){ return new _Sum(x) }
  • 76. _Sum.prototype.concat = function(y){ return Sum(this.val + y.val) } _Sum.prototype.empty = function(){ return Sum(0) } var sum = function(xs) { return reduce((acc, x) => acc + x, 0, xs) }
  • 77. _Sum.prototype.concat = function(y){ return Sum(this.val + y.val) } _Sum.prototype.empty = function(){ return Sum(0) } var sum = function(xs) { return reduce((acc, x) => acc + x, 0, xs) }
  • 78. fold([Sum(1), Sum(2), Sum(3), Sum(4)]) //=> Sum(10) _Sum.prototype.concat = function(y){ return Sum(this.val + y.val) } _Sum.prototype.empty = function(){ return Sum(0) }
  • 79. fold([Product(1),Product(2),Product(3),Product(4)]) //=> Product(24) _Product.prototype.concat = function(y){ return Product(this.val * y.val) } _Product.prototype.empty = function(){ return Product(1) }
  • 80. fold([Max(11),Max(16),Max(3),Max(9)]) //=> Max(16) _Max.prototype.concat = function(y){ return Max(this.val > y.val ? this.val : y.val) } _Max.prototype.empty = function(){ return Max(-Infinity) }
  • 81. _All.prototype.concat = function(y){ return Any(this.val && y.val) } _All.prototype.empty = function(){ return All(true) } fold([All(false), All(false), All(true), All(false)]) //=> All(false)
  • 82. _Any.prototype.concat = function(y){ return Any(this.val || y.val) } _Any.prototype.empty = function(){ return Any(false) } fold([Any(false), Any(false), Any(true), Any(false)]) //=> Any(true)
  • 84. _Any.prototype.concat = function(y){ return Any(this.val || y.val) } _Any.prototype.empty = function(){ return Any(false) } fold([Any(false), Any(false), Any(true), Any(false)]) //=> Any(true)
  • 85. var foldMap = function(f,xs) { return compose(fold, map(f))(xs); } var sum = function(xs){ return foldMap(Sum, xs).val } var max = function(xs){ return foldMap(Max, xs).val } var any = function(xs){ return foldMap(Any, xs).val }
  • 86. var tree = Node(Node(Leaf(2), 1, Leaf(3)), 2, Leaf(4)) sum(tree) //=> 12 product(tree) //=> 38 max(tree) //=> 4
  • 87. var lst = Cons(true, Cons(false, Cons(true, Nil))) any(lst) //=> true all(lst) //=> false
  • 90. [Sum(a)] -> Sum(a) [Product(a)] -> Product(a) [Max(a)] -> Max(a)
  • 94. var cata = function(f, xs) { return f(xs.map(ys => cata(f,ys))) }
  • 95. var cata = function(f, xs) { return f(xs.map(ys => cata(f,ys))) }
  • 96. var cata = function(f, xs) { return f(xs.map(ys => cata(f,ys))) }
  • 97. Fixed point of a Functor
  • 98. Nil.map = function(f) { return Nil; } _Cons.prototype.map = function(f) { return Cons(this.head, f(this.tail)) }
  • 100. var sum = function(x) { return (x === Nil) ? 0 : x.head + x.tail } var lst = Cons(2, Cons(3, Cons(4, Nil))); cata(sum, lst); //=> 9
  • 101. var sum = function(x) { return (x === Nil) ? 0 : x.head + x.tail } var lst = Cons(2, Cons(3, Cons(4, Nil))); cata(sum, lst); //=> 9 Algebra
  • 102. var sum = function(x) { return (x === Nil) ? 0 : x.head + x.tail } var lst = Cons(2, Cons(3, Cons(4, Nil))); cata(sum, lst); // Nil // Cons(4, 0) // Cons(3, 4) // Cons(2, 7) //=> 9
  • 103. map(x => x + 1, Cons(2, Cons(3, Cons(4, Nil)))) //=> Cons(3, Cons(4, Cons(5, Nil))) var map = function(f, xs) { return cata(x => (x == Nil) ? Nil : Cons(f(x.head), x.tail), xs) }
  • 104. Empty.map = function(f) { return Empty } _Leaf.prototype.map = function(f) { return Leaf(this.x) } _Node.prototype.map = function(f) { return Node(f(this.left), this.x, f(this.right)) }
  • 105. cata(t => switch (t.constructor) { case _Node: return t.left + t.x + t.right; case _Leaf: return t.x; default: 0; }, tr) //=> 10 var tr = Node(Node(Leaf(2), 1, Leaf(3)), 0, Leaf(4))
  • 106. var ana = function(g, a) { return g(a).map(x => ana(g,x)) }
  • 107. var ana = function(g, a) { return g(a).map(x => ana(g,x)) }
  • 108. var arrToList = function(xs) { return xs.length === 0 ? Nil : Cons(first(xs), rest(xs)) } ana(arrToList, [1,2,3,4,5]) //=> Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Nil))))) CoAlgebra
  • 109. var arrToList = function(xs) { return xs.length === 0 ? Nil : Cons(first(xs), rest(xs)) } ana(arrToList, [1,2,3,4,5]) //=> Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Nil)))))
  • 111. var arrToList = function(xs) { return xs.length === 0 ? Nil : Cons(first(xs), rest(xs)) } ana(arrToList, [1,2,3,4,5]) //=> Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Nil)))))
  • 112. var makeAlphabet = function(x) { if(x > 25) return Nil; return Cons(String.fromCharCode(x+65), x+1); } ana(makeAlphabet, 0) //=> Cons(A, Cons(B, Cons(C, Cons(D, Cons(E, Cons(F, Cons(G, Cons(H
  • 113. var range = function(acc, count) { return ana(x => (x >= count) ? Nil : Cons(x, x+1), acc) } range(2, 10) //=> Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Cons(7, Cons(8, Cons(9, Nil))))))))
  • 119. var _Const = function(val) { this.val = val } var Const = function(x) { return new _Const(x) } var _Add = function(x, y) { this.x = x; this.y = y; } var Add = function(x, y) { return new _Add(x, y) } var _Mul = function(x, y) { this.x = x this.y = y } var Mul = function(x, y) { return new _Mul(x, y) }
  • 120. _Const.prototype.map = function(f) { return this } _Add.prototype.map = function(f) { return Add(f(this.x), f(this.y)) } _Mul.prototype.map = function(f) { return Mul(f(this.x), f(this.y)) }
  • 121. var interpret = function(a) { switch(a.constructor) { case _Mul: return a.x * a.y; case _Add: return a.x + a.y; case _Const: return a.val; } } var program = Mul(Add(Const(2), Const(3)), Const(4)) cata(interpret, program); //=> 20
  • 122. var _Concat = function(v, next) { this.val = v; this.next = next; } var Concat = function(v, x){ return new _Concat(v, x) } var _Replace = function(v, x, next) { this.val = v; this.x = x; this.next = next; } var Replace = function(v, x, nt){ return new _Replace(v, x, nt) } var _Input = function(v) { this.val = v } var Input = function(v){ return new _Input(v) }
  • 123. _Concat.prototype.map = function(f) { return Concat(this.val, f(this.next)) } _Replace.prototype.map = function(f) { return Replace(this.val, this.x, f(this.next)) } _Input.prototype.map = function(f) { return Input(this.val) }
  • 124. var interpret = function(t) { switch (t.constructor) { case _Concat: return t.next.concat(t.val); case _Replace: return t.next.replace(t.val, t.x); case _Input: return t.val; } } var prog = Concat("world", Replace("h", "m", Input(“hello"))) cata(interpret, prog) //=> melloworld
  • 125. var interpret1 = function(t) { switch (t.constructor) { case _Concat: return "concatting “+t.val+" after "+t.next; case _Replace: return "replacing "+t.val+" with "+t.x+" on "+t.next; case _Input: return t.val; } } var prog = Concat("world", Replace("h", "m", Input(“hello"))) cata(interpret1, prog) //=> concatting world after replacing h with m on hello