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

PPT
Functional Patterns for the non-mathematician
PPTX
Oh Composable World!
PDF
ScalaMeter 2014
PDF
PDF
Introduction to Scala
PPTX
Kotlin collections
PPTX
Kotlin standard
PDF
Programmation fonctionnelle en JavaScript
Functional Patterns for the non-mathematician
Oh Composable World!
ScalaMeter 2014
Introduction to Scala
Kotlin collections
Kotlin standard
Programmation fonctionnelle en JavaScript

What's hot (18)

PPTX
하스켈 프로그래밍 입문 3
KEY
関数潮流(Function Tendency)
PPTX
Groovy puzzlers по русски с Joker 2014
PDF
Scala Parallel Collections
PPTX
하스켈 프로그래밍 입문 4
PDF
Scala by Luc Duponcheel
PPT
Functional programming in scala
PDF
Implementing virtual machines in go & c 2018 redux
PDF
JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...
PDF
Hitchhiker's Guide to Functional Programming
DOCX
imager package in R and examples..
PPTX
Kotlin class
DOCX
Advanced Data Visualization Examples with R-Part II
PDF
Stanfy MadCode Meetup #9: Functional Programming 101 with Swift
PPTX
The Groovy Puzzlers – The Complete 01 and 02 Seasons
ODP
2.3 implicits
PDF
Transducers in JavaScript
PDF
Scala collections
하스켈 프로그래밍 입문 3
関数潮流(Function Tendency)
Groovy puzzlers по русски с Joker 2014
Scala Parallel Collections
하스켈 프로그래밍 입문 4
Scala by Luc Duponcheel
Functional programming in scala
Implementing virtual machines in go & c 2018 redux
JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...
Hitchhiker's Guide to Functional Programming
imager package in R and examples..
Kotlin class
Advanced Data Visualization Examples with R-Part II
Stanfy MadCode Meetup #9: Functional Programming 101 with Swift
The Groovy Puzzlers – The Complete 01 and 02 Seasons
2.3 implicits
Transducers in JavaScript
Scala collections
Ad

Viewers also liked (8)

PPTX
Fact, Fiction, and FP
KEY
Functional Reactive Programming in Javascript
PPTX
Ricky Bobby's World
PPTX
KEY
Functional js class
PPT
Js for learning
PPT
Liftin every day
PPS
Underscore
Fact, Fiction, and FP
Functional Reactive Programming in Javascript
Ricky Bobby's World
Functional js class
Js for learning
Liftin every day
Underscore
Ad

Similar to Millionways (20)

PDF
All You Need is Fold
PPTX
ECMA5 and ES6 Promises
PDF
Functional Programming: An Introduction
PDF
From Javascript To Haskell
PDF
JSDC 2014 - functional java script, why or why not
PDF
Monads from Definition
PDF
Hardcore functional programming
PDF
Category Theory made easy with (ugly) pictures
PPTX
The Essence of the Iterator Pattern
PDF
Declare Your Language: Constraint Resolution 2
PDF
The Essence of the Iterator Pattern (pdf)
PDF
Coscup2021 - useful abstractions at rust and it's practical usage
PDF
ECMAScript 6 major changes
PPTX
Academy PRO: ES2015
PDF
Essence of the iterator pattern
PDF
Monad presentation scala as a category
PPTX
An Introduction to Functional Programming with Javascript
PPTX
Dr iterate
PDF
Thinking Functionally In Ruby
PDF
Formal methods 4 - Z notation
All You Need is Fold
ECMA5 and ES6 Promises
Functional Programming: An Introduction
From Javascript To Haskell
JSDC 2014 - functional java script, why or why not
Monads from Definition
Hardcore functional programming
Category Theory made easy with (ugly) pictures
The Essence of the Iterator Pattern
Declare Your Language: Constraint Resolution 2
The Essence of the Iterator Pattern (pdf)
Coscup2021 - useful abstractions at rust and it's practical usage
ECMAScript 6 major changes
Academy PRO: ES2015
Essence of the iterator pattern
Monad presentation scala as a category
An Introduction to Functional Programming with Javascript
Dr iterate
Thinking Functionally In Ruby
Formal methods 4 - Z notation

Recently uploaded (20)

PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PPTX
Cloud computing and distributed systems.
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PPTX
Big Data Technologies - Introduction.pptx
PDF
Electronic commerce courselecture one. Pdf
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
cuic standard and advanced reporting.pdf
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
CIFDAQ's Market Insight: SEC Turns Pro Crypto
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
DOCX
The AUB Centre for AI in Media Proposal.docx
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Encapsulation theory and applications.pdf
PDF
Modernizing your data center with Dell and AMD
PDF
KodekX | Application Modernization Development
Building Integrated photovoltaic BIPV_UPV.pdf
Cloud computing and distributed systems.
The Rise and Fall of 3GPP – Time for a Sabbatical?
Big Data Technologies - Introduction.pptx
Electronic commerce courselecture one. Pdf
Mobile App Security Testing_ A Comprehensive Guide.pdf
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
cuic standard and advanced reporting.pdf
NewMind AI Weekly Chronicles - August'25 Week I
Advanced methodologies resolving dimensionality complications for autism neur...
Reach Out and Touch Someone: Haptics and Empathic Computing
Chapter 3 Spatial Domain Image Processing.pdf
CIFDAQ's Market Insight: SEC Turns Pro Crypto
Dropbox Q2 2025 Financial Results & Investor Presentation
The AUB Centre for AI in Media Proposal.docx
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Encapsulation theory and applications.pdf
Modernizing your data center with Dell and AMD
KodekX | Application Modernization Development

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