Slide 1

Slide 1 text

The Way to Fantasyland CT Wu JSDC 2017 wu_ct wuct

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

Divide and Conquer

Slide 4

Slide 4 text

A good abstraction should be intuitive and universal.

Slide 5

Slide 5 text

Mathematic

Slide 6

Slide 6 text

(1 + 2) + 3 = 1 + (2 + 3) (1 * 2) * 3 = 1 * (2 * 3)

Slide 7

Slide 7 text

(a ∨ b) ∨ c = a ∨ (b ∨ c) (a ∧ b) ∧ c = a ∧ (b ∧ c)

Slide 8

Slide 8 text

max(max(1, 2), 3) = max(1, max(2, 3)) min(min(1, 2), 3) = min(1, min(2, 4))

Slide 9

Slide 9 text

gcd(gcd(2, 4), 6) = gcd(2, gcd(4, 6)) lcm(lcm(2, 4), 6) = lcm(2, lcm(4, 6))

Slide 10

Slide 10 text

The associativity is universal.

Slide 11

Slide 11 text

([1].concat([2])).concat([3]) = [1].concat([2].concat([3])) ("1".concat("2")).concat("3") = "1".concat("2".concat("3")) Even in Programming Languages

Slide 12

Slide 12 text

Can we define this kind of abstractions in programming languages?

Slide 13

Slide 13 text

It is functional programming.

Slide 14

Slide 14 text

The name of the abstraction is Semigroup (1 + 2) + 3 = 1 + (2 + 3) (1 * 2) * 3 = 1 * (2 * 3) max(max(2, 4), 6) = max(2, max(4, 6)) min(min(2, 4), 6) = min(2, min(4, 6)) gcd(gcd(2, 4), 6) = gcd(2, gcd(4, 6)) lcm(lcm(2, 4), 6) = lcm(2, lcm(4, 6)) (a ∨ b) ∨ c = a ∨ (b ∨ c) (a ∧ b) ∧ c = a ∧ (b ∧ c) Value Operation

Slide 15

Slide 15 text

Fantasyland Land Specification “Algebraic JavaScript Specification” An algebra is a set of values, a set of operators that it is closed under and some laws it must obey.

Slide 16

Slide 16 text

The Genealogy of Fantasyland

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

#:: “is a member of” "fantasy" #:: String New types can be created via type constructors ["fantasy"] #:: Array String #[["fantasy"], ["land"#]] #:: Array (Array String) Type Signature Notation in Fantasyland

Slide 19

Slide 19 text

#-> is an infix type constructor String #-> Number A function with multiple #-> is a curried function String #-> String #-> Number Type Signature Notation in Fantasyland

Slide 20

Slide 20 text

Lowercase letters stand for type variables a #-> b #~> is an infix type constructor of a method a #~> b () stands for no argument () #-> a #=> expresses constraints on type variables Semigroup a #=> a #-> a Type Signature Notation in Fantasyland

Slide 21

Slide 21 text

concat #:: Semigroup a #=> a #~> a #-> a Let’s Define Semigroup x.concat(y).concat(z) = x.concat(y.concat(z)) concat should satisfy the associative law…

Slide 22

Slide 22 text

[1].concat([2]).concat([3]) "1".concat("2").concat("3") Array and String are already Semigroup by default In fact, we still need to add “fantasy-land/“ prefix to methods name, but it’s an implementation detail.

Slide 23

Slide 23 text

const daggy = require('daggy') const Sum = daggy.tagged('Sum', ['value']) Sum(1) #// = { value: 1 } Sum(1).toString() #// = "Sum(1)" Daggy is an utility for creating constructors with named properties. Daggy The Utility

Slide 24

Slide 24 text

const Sum = daggy.tagged('Sum', ['value']) Sum.prototype.concat = function(that) { return Sum(this.value + that.value) } Sum(1).concat(Sum(2)).concat(Sum(3)) #// = Sum(6) Sum as an Instance of Semigroup

Slide 25

Slide 25 text

const All = daggy.tagged('All', [‘value']) All.prototype.concat = function(that) { return All(this.value #&& that.value) } All(false).concat(All(false)).concat(All(true)) #// = All(false) Boolean as an Instance of Semigroup (All)

Slide 26

Slide 26 text

const Any = daggy.tagged('Any', [‘value']) Any.prototype.concat = function(that) { return Any(this.value #|| that.value) } Any(false).concat(Any(false)).concat(Any(true)) #// = Any(true) Boolean as an Instance of Semigroup (Any)

Slide 27

Slide 27 text

const List = daggy.taggedSum('List', { Cons: ['head', 'tail'], #// Cons #:: a #-> List a #-> List a Nil: [] #// Nil #:: List a }) List.prototype.concat = function(that) { return this.cata({ Cons: (head, tail) #=> List.Cons(head, tail.concat(that)), Nil: () #=> that }) } List.Cons(1, List.Nil).concat(List.Cons(2, List.Nil)) #// = List.Cons(1, List.Cons(2, List.Nil)) Linked List as an Instance of Semigroup

Slide 28

Slide 28 text

const First = daggy.tagged('First', ['value']) First.prototype.concat = function() { return this } First("A").concat(First("B").concat("C") #// = First("A") const Last = daggy.tagged('Last', ['value']) Last.prototype.concat = function(that) { return that } Last("A").concat(Last("B")).concat(Last("C")) #// = Last("B") More Semigroup Examples

Slide 29

Slide 29 text

How can these things be useful?

Slide 30

Slide 30 text

Let’s See a Real World Usage Example • You have a website providing several ways for users to sign up, e.g. email/password and social login. • An user creating two accounts accidentally wants to merge them now. • An user account can be an instance of Semigroup!

Slide 31

Slide 31 text

const User = daggy.tagged('User', ['name', 'emails', 'isVerified', 'loginTimes']) User.prototype.concat = function(that) { return User( this.name.concat(that.name), this.emails.concat(that.emails), this.isVerified.concat(that.isVerified), this.loginTimes.concat(that.loginTimes) ) } User(First('CT'), ['[email protected]'], Any(false), Sum(6)) .concat( User(First('CT Wu'), ['[email protected]'], Any(true), Sum(13)) ) #// = User(First("CT"), ["[email protected]", "[email protected]"], Any(true), Sum(19)) User as an Instance of Semigroup

Slide 32

Slide 32 text

How about parallel computing?

Slide 33

Slide 33 text

We can code logic into types! • Types can be checked by compilers, so there’s no need for unit tests. • First, Any, Sum and other types we used here can be provided by libraries. • Functional programming languages, like PureScript, already providing these common types in the language.

Slide 34

Slide 34 text

Can a set be an instance of Semigroup?

Slide 35

Slide 35 text

(A ∪ B) ∪ C = A ∪ (B ∪ C) (A ∩ B) ∩ C = A ∩ (B ∩ C)

Slide 36

Slide 36 text

Set as an instance of Semigroup • Union and intersection are associative operations. • However, we need to be capable of comparing two elements are equal or not. • Setoid is the algebra!

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

Setoid equals #:: Setoid a #=> a #~> a #-> Boolean equals should satisfy following laws: Reflexivity: a.equals(a) ##=== true Commutativity: a.equals(b) ##=== b.equals(a) Transitivity: if a.equals(b) and b.equals(c) then a.equals(c)

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

Monoid empty #:: Monoid m #=> () #-> m … and the laws: Right Identity: m.concat(M.empty()) is equivalent to m Left Identity: M.empty().concat(m) is equivalent to m

Slide 41

Slide 41 text

Some Monoid implementation… String.empty = () #=> "" Sum.empty = () #=> Sum(0) Product.empty = () #=> Product(1) All.empty = () #=> All(true) Any.empty = () #=> Any(false) Max.empty = () #=> Max(-Infinity) Min.empty = () #=> Min(Infinity) Array.empty = () #=> [] List.empty = () #=> List.Nil First.empty = () #=> #// ? Last.empty = () #=> #// ?

Slide 42

Slide 42 text

Reduce Data by the Monoid operation foldArray #:: Monoid m #=> Array m #-> m foldArray = array #=> array.reduce((a, b) #=> a.concat(b), []) foldArray(["A", "B", "C"]) #// = "ABC" foldArray([Sum(1), Sum(2)]) #// = Sum(3) In this sense, we could fold other types by…
 foldList #:: Monoid m #=> List m #-> m foldTree #:: Monoid m #=> Tree m #-> m if they can be reduced.

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

Foldable reduce #:: Foldable f #=> f a #~> ((b, a) #-> b, b) #-> b … and there is no law for Foldable!

Slide 45

Slide 45 text

List.prototype.reduce = function(f, initial) { return this.cata({ Cons: (head, tail) #=> tail.reduce(f, f(initial, head)), Nil: () #=> initial, }) } List.Cons(1, List.Cons(2, List.Cons(3, List.Nil))) .reduce((a, b) #=> a + b, 0) #// = 6 Linked List as an Instance of Foldable

Slide 46

Slide 46 text

fold #:: Foldable f #=> Monoid m #=> f m #-> m fold = M #=> foldable #=> foldable.reduce((acc, x) #=> acc.concat(x), M.empty()) fold(Sum)(List.Cons(Sum(1), List.Cons(Sum(2), List.Nil))) #// = Sum(3) fold(All)([All(true), All(false)]) #// = All(false) Fold Universally

Slide 47

Slide 47 text

The force of abstraction.

Slide 48

Slide 48 text

Let’s see another abstraction const inc = x #=> x + 1 [1].map(inc) #// = [2] Promise.resolve(1).then(inc) #// = Promise.resolve(2) Observable.of(1).map(inc) #// = Observable.of(2) They are similar … as usual, there is an abstraction over them already!

Slide 49

Slide 49 text

No content

Slide 50

Slide 50 text

Functor map #:: Functor f #=> f a #~> (a #-> b) #-> f b … and the laws Identity: u.map(x #=> x) ##=== u Composition: u.map(f).map(g) ##=== u.map(x #=> g(f(x)))

Slide 51

Slide 51 text

The Intuition of Functor [1] #:: Array Int Promise.resolve(1) #:: Promise Int Observable.of(1) #:: Observable Int 1 2 [2] #:: Array Int Promise.resolve(2) #:: Promise Int Observable.of(2) #:: Observable Int map(x #=> x + 1) Notice that the types of Promise and Observable are overly simplified here. We should also consider error handling in their types.

Slide 52

Slide 52 text

Can Set be an instance of Functor?

Slide 53

Slide 53 text

No, because every element in a set should be a Setoid. map #:: Functor f #=> f a #~> (a #-> b) #-> f b b might be not a Setoid.

Slide 54

Slide 54 text

Have You Seen This? if (a #!= undefined) { if (b #!= undefined) { if (c #!= undefined) { } } } Or these… TypeError: Cannot read property ‘…’ of undefined TypeError: … is not a function

Slide 55

Slide 55 text

Another Useful Functor, Maybe const Maybe = daggy.taggedSum('Maybe', { Just: ['value'], Nothing: [] }) Maybe.prototype.map = function(f) { return this.cata({ Just: value #=> Maybe.Just(f(value)), Nothing: () #=> Maybe.Nothing }) } Maybe.Nothing.map(x #=> x.length) #// = Maybe.Nothing Maybe.Just([1, 2, 3]).map(x #=> x.length) #// = Maybe.Just(3)

Slide 56

Slide 56 text

Function as an Instance of Functor Functor is an abstraction over unary type constructors… Functor f #=> f a #~> (a #-> b) #-> f b We know (#->) is a binary type constructor, 
 so (#->) r is an unary type constructor… ((#->) r a) #~> (a #-> b) #-> ((#->) r b) (r #-> a) #~> (a #-> b) #-> (r #-> b)

Slide 57

Slide 57 text

Function as an Instance of Functor Function.prototype.map = function(f) { return x #=>f(this(x)) } const f = (x #=> x * 10).map(x #=> x + 7) f(8) #// = ?

Slide 58

Slide 58 text

Compose is Just Functor Map map #:: Functor f #=> (a #-> b) #-> f a #-> f b map #:: (a #-> b) #-> (r #-> a) #-> (r #-> b) map = (f, g) #=> x #=> f(g(x)) #// = compose

Slide 59

Slide 59 text

Learn once, apply everywhere.

Slide 60

Slide 60 text

Question? wu_ct wuct