1 of 79

Awesomely descriptive JavaScript

with Monads

2 of 79

Theory

3 of 79

Monad laws

return a >>= f f a

m >>= return m

(m >>= f) >>= g m >>= (\x -> f x >>= g)

4 of 79

Monad laws

5 of 79

Practical theory

6 of 79

7 of 79

V E G E T A B L E

A

L

U

E

8 of 79

9 of 79

10 of 79

P => M<S>

f

11 of 79

=>

unit

P => M<P>

12 of 79

( , ) =>

bind

( M<P> , P => M<S> ) => M<S>

13 of 79

( M<P> , P => M<S> ) => M<S>

M<P>

P => M<S>

M<S>

extract value

bind function

m(x)

x

x => m(y)

m(y)

bind

14 of 79

Monad laws (JavaScript)

bind( unit(v) , f ) f(v)

bind( monad , unit ) monad

bind( bind( monad , f ) , g )

bind( monad, v => bind( f(v) , g ) )

15 of 79

monet.js

16 of 79

UNIT

BIND

Identity(x)

Maybe.Some(x)

Maybe.None()

Either.Left(e)

Either.Right(x)

List(x, Nil)

monad.bind(f)

monad.flatMap(f)

monad.chain(f)

17 of 79

Monad laws (monet.js / Identity)

Identity(v).flatMap(f) f(v)

id.flatMap(Identity) id

id.flatMap(f).flatMap( g )

id.flatMap(v => f(v).flatMap(g))

18 of 79

Practice: Identity

19 of 79

UNIT

BIND

Identity(x)

Maybe.Some(x)

Maybe.None()

Either.Left(e)

Either.Right(x)

List(x, Nil)

monad.bind(f)

monad.flatMap(f)

monad.chain(f)

20 of 79

var pepperInABowl = getBowlOf('pepper', 'red');

var pepper = pepperInABowl.get();

var slicedPepperInABowl = slice(pepper);

var tomatoInABowl = getBowlOf('tomato');

var tomato = tomatoInABowl.get();

var slicedTomatoInABowl = slice(tomato);

var slicedPepper = slicedPepperInABowl.get();

var slicedTomato = slicedTomatoInABowl.get();

var mixInABowl = mix(slicedPepper, slicedTomato);

21 of 79

22 of 79

const mixInABowl = getBowlOf('pepper')

.flatMap(slice)

.faltMap(silcedPepper => getBowlOf('tomato')

.flatMap(slice)

.faltMap(mixedTomato =>

mix(silcedPepper , mixedTomato)));

23 of 79

24 of 79

But HOW ?

25 of 79

interface Bowl<V> {

get(): V;

}

getBowlOf<V>(name: string): Bowl<V>;

slice<S>(vegetable: Vege): Bowl<S>;

mix<M>(...ingredients): Bowl<M>;

26 of 79

var pepperInABowl = getBowlOf('pepper');

var pepper = pepperInABowl.get();

var slicedPepperInABowl = slice(pepper);

var tomatoInABowl = getBowlOf('tomato');

var tomato = tomatoInABowl.get();

var slicedTomatoInABowl = slice(tomato);

var slicedPepper = slicedPepperInABowl.get();

var slicedTomato = slicedTomatoInABowl.get();

var mixInABowl = mix(slicedPepper, slicedTomato);

27 of 79

var pepperInABowl = getBowlOf('pepper');

var pepper = pepperInABowl.get();

var slicedPepperInABowl = slice(pepper);

var tomatoInABowl = getBowlOf('tomato');

var tomato = tomatoInABowl.get();

var slicedTomatoInABowl = slice(tomato);

var slicedPepper = slicedPepperInABowl.get();

var slicedTomato = slicedTomatoInABowl.get();

var mixInABowl = mix(slicedPepper, slicedTomato);

28 of 79

var pepperInABowl = getBowlOf('pepper');

var pepper = pepperInABowl.get();

var slicedPepperInABowl = slice(pepper);

var tomatoInABowl = getBowlOf('tomato');

var tomato = tomatoInABowl.get();

var slicedTomatoInABowl = slice(tomato);

var slicedPepper = slicedPepperInABowl.get();

var slicedTomato = slicedTomatoInABowl.get();

var mixInABowl = mix(slicedPepper, slicedTomato);

29 of 79

var pepperInABowl = getBowlOf('pepper');

var pepper = pepperInABowl.get();

var slicedPepperInABowl = slice(pepper);

var tomatoInABowl = getBowlOf('tomato');

var tomato = tomatoInABowl.get();

var slicedTomatoInABowl = slice(tomato);

var slicedPepper = slicedPepperInABowl.get();

var slicedTomato = slicedTomatoInABowl.get();

var mixInABowl = mix(slicedPepper, slicedTomato);

30 of 79

monet.js

Identity( … )

31 of 79

type Op<A, B> = (val:A) => Bowl<B>;

interface Bowl<V> {

get(): V;

flatMap<S>(f: Op<V, S>): Bowl<S>;

}

32 of 79

33 of 79

var pepperInABowl = getBowlOf('pepper');

var pepper = pepperInABowl.get();

var slicedPepperInABowl = slice(pepper);

var tomatoInABowl = getBowlOf('tomato');

var tomato = tomatoInABowl.get();

var slicedTomatoInABowl = slice(tomato);

var slicedPepper = slicedPepperInABowl.get();

var slicedTomato = slicedTomatoInABowl.get();

var mixInABowl = mix(slicedPepper, slicedTomato);

34 of 79

const slicedPepperInABowl =

getBowlOf('pepper').flatMap(slice);

35 of 79

var pepperInABowl = getBowlOf('pepper');

var pepper = pepperInABowl.get();

var slicedPepperInABowl = slice(pepper);

var tomatoInABowl = getBowlOf('tomato');

var tomato = tomatoInABowl.get();

var slicedTomatoInABowl = slice(tomato);

var slicedPepper = slicedPepperInABowl.get();

var slicedTomato = slicedTomatoInABowl.get();

var mixInABowl = mix(slicedPepper, slicedTomato);

36 of 79

const slicedPepperInABowl =

getBowlOf('pepper').flatMap(slice);

const slicedTomatoInABowl =

getBowlOf('tomato').flatMap(slice);

37 of 79

var pepperInABowl = getBowlOf('pepper');

var pepper = pepperInABowl.get();

var slicedPepperInABowl = slice(pepper);

var tomatoInABowl = getBowlOf('tomato');

var tomato = tomatoInABowl.get();

var slicedTomatoInABowl = slice(tomato);

var slicedPepper = slicedPepperInABowl.get();

var slicedTomato = slicedTomatoInABowl.get();

var mixInABowl = mix(slicedPepper, slicedTomato);

38 of 79

const slicedPepperInABowl =

getBowlOf('pepper').flatMap(slice);

const mixInABowl = getBowlOf('pepper')

.flatMap(slice)

.faltMap(silcedPepper =>

mix(silcedPepper , slicedPepperInABowl.get()));

39 of 79

const slicedPepperInABowl =

getBowlOf('pepper').flatMap(slice);

const mixInABowl = getBowlOf('pepper')

.flatMap(slice)

.flatMap(silcedPepper => slicedPepperInABowl

.flatMap(mixedTomato =>

mix(silcedPepper , mixedTomato)));

40 of 79

const mixInABowl = getBowlOf('pepper')

.flatMap(slice)

.faltMap(silcedPepper => getBowlOf('tomato')

.flatMap(slice)

.flatMap(mixedTomato =>

mix(silcedPepper , mixedTomato)));

41 of 79

var pepperInABowl = getBowlOf('pepper', 'red');

var pepper = pepperInABowl.get();

var slicedPepperInABowl = slice(pepper);

var tomatoInABowl = getBowlOf('tomato');

var tomato = tomatoInABowl.get();

var slicedTomatoInABowl = slice(tomato);

var slicedPepper = slicedPepperInABowl.get();

var slicedTomato = slicedTomatoInABowl.get();

var mixInABowl = mix(slicedPepper, slicedTomato);

42 of 79

Practice: Maybe

43 of 79

UNIT

BIND

Identity(x)

Maybe.Some(x)

Maybe.None()

Either.Left(e)

Either.Right(x)

List(x, Nil)

monad.bind(f)

monad.flatMap(f)

monad.chain(f)

44 of 79

45 of 79

let spiceMix;

const salt = getSpice('salt');

let coriander = getSpice('coriander');

if (coriander) {

coriander = crush(coriander);

}

if (coriander && salt && isPowdered(salt)) {

spiceMix = mixSpice(salt , coriander);

}

spiceMix = vegeta;

46 of 79

let spiceMix;

const salt = getSpice('salt');

let coriander = getSpice('coriander');

if (coriander) {

coriander = crush(coriander);

}

if (coriander && salt && isPowdered(salt)) {

spiceMix = mixSpice(salt , coriander);

}

spiceMix = vegeta;

47 of 79

const spiceMix = getSpice('salt').filter(isPowdered)

.flatMap(salt =>

getSpice('coriander').map(crush)

.flatMap(coriander =>

mixSpice(salt , coriadner)))

.orJust(vegeta);

48 of 79

But HOW ?

49 of 79

getSpice<V>(name: string): Maybe<V>;

crush(spice: S): S;

isPowdered(spice: S): boolean;

50 of 79

51 of 79

getSpice<V>(name: string): Maybe<V>;

crush(spice: S): S;

isPowdered(spice: S): boolean;

52 of 79

53 of 79

getSpice<V>(name: string): Maybe<V>;

crush(spice: S): S;

isPowdered(spice: S): boolean;

54 of 79

monet.js

Maybe

55 of 79

interface Maybe<T> {

flatMap<V>(fn: (val: T) => Maybe<V>): Maybe<V>;

map<V>(fn: (val: T) => V): Maybe<V>;

filter(fn: (val: T) => boolean): Maybe<T>;

orJust(val: T): T;

}

56 of 79

const silkySalt = getSpice('salt').filter(isPowdered);

const crushedCoriander = getSpice('coriander').map(crush);

const spiceMixMaybe = silkySalt.flatMap(salt =>

crushedCoriander.flatMap(coriander =>

mixSpice( salt , coriadner )));

const spiceMix = spiceMixMaybe.orJust(vegeta);

57 of 79

const crushedCoriander = getSpice('coriander').map(crush);

const spiceMixMaybe =

getSpice('salt').filter(isPowdered).flatMap(salt =>

crushedCoriander.flatMap(coriander =>

mixSpice( salt , coriadner )));

const spiceMix = spiceMixMaybe.orJust(vegeta);

58 of 79

const spiceMixMaybe =

getSpice('salt').filter(isPowdered).flatMap(salt =>

getSpice('coriander').map(crush).flatMap(coriander =>

mixSpice( salt , coriadner )));

const spiceMix = spiceMixMaybe.orJust(vegeta);

59 of 79

const spiceMixMaybe = getSpice('salt').filter(isPowdered)

.flatMap(salt =>

getSpice('coriander').map(crush)

.flatMap(coriander =>

mixSpice( salt , coriadner )));

const spiceMix = spiceMixMaybe.orJust(vegeta);

60 of 79

const spiceMix = getSpice('salt').filter(isPowdered)

.flatMap(salt =>

getSpice('coriander').map(crush)

.flatMap(coriander =>

mixSpice( salt , coriadner )));

.orJust(vegeta)

61 of 79

const spiceMix = getSpice('salt')

.filter(isPowdered)

.flatMap(salt =>

getSpice('coriander')

.map(crush)

.flatMap(coriander =>

mixSpice(salt , coriadner)))

.orJust(vegeta);

62 of 79

null

63 of 79

getSpice<V>(name: string): Maybe<V> {

let spice = locker.get(name);

if (spice != null) {

return Some(spice);

}

return None();

}

64 of 79

getSpice<V>(name: string): Maybe<V> {

const spice = locker.get(name);

if (spice != null) {

return Some(spice);

}

return None();

}

65 of 79

getSpice<V>(name: string): Maybe<V> {

const spice = locker.get(name);

if (spice != null) {

return … ?

}

return None();

}

66 of 79

monet.js

Some( … )

67 of 79

getSpice<V>(name: string): Maybe<V> {

const spice = locker.get(name);

if (spice != null) {

return Some(spice);

}

return None();

}

68 of 79

monet.js

None()

69 of 79

getSpice<V>(name: string): Maybe<V> {

const spice = locker.get(name);

if (spice != null) {

return Some(spice);

}

return None();

}

70 of 79

monet.js

.fromNull()

71 of 79

getSpice<V>(name: string): Maybe<V> {

return Maybe.fromNull(

locker.get(name)

);

}

72 of 79

73 of 79

goo.gl/cvGVeo

74 of 79

const saladBowl = mixInABowl

.flatMap(mixedVegetables =>

mix(mixedVegetables, spiceMix));

75 of 79

e a t i t

76 of 79

Monads

Identity

Maybe

Either

Validation

List

NEL

Reader

Free

IO

?

77 of 79

Monads

Identity

Maybe

Either

Validation

List

NEL

Reader

Free

IO

Promise

78 of 79

T H X

79 of 79

Jakub . Strojewski @gmail.com

u l f r y k