awesomely descriptive javascript with monads
TRANSCRIPT
Awesomely descriptive JavaScriptwith Monads
Theory
Monad laws
return a >>= f ≡ f a
m >>= return ≡ m
(m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)
Monad laws
Practical theory
V E G E T A B L EALUE
P => M<S>
f
=>
unit
P => M<P>
( , ) =>
bind
( M<P> , P => M<S> ) => M<S>
( M<P> , P => M<S> ) => M<S>M<P> P => M<S> M<S>
extract value
bind functio
n
m(x)
x
x => m(y) m(y)
bind
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 ) )
monet.js
UNIT
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)
BIND
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))
Practice: Identity
UNIT
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)
BIND
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);
const mixInABowl = getBowlOf('pepper') .flatMap(slice) .faltMap(silcedPepper => getBowlOf('tomato') .flatMap(slice) .faltMap(mixedTomato => mix(silcedPepper , mixedTomato)));
But HOW ?
interface Bowl<V> { get(): V;}getBowlOf<V>(name: string): Bowl<V>;slice<S>(vegetable: Vege): Bowl<S>;mix<M>(...ingredients): Bowl<M>;
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);
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);
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);
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);
monet.js
Identity( … )
type Op<A, B> = (val:A) => Bowl<B>;
interface Bowl<V> { get(): V; flatMap<S>(f: Op<V, S>): Bowl<S>;}
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);
const slicedPepperInABowl = getBowlOf('pepper').flatMap(slice);
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);
const slicedPepperInABowl = getBowlOf('pepper').flatMap(slice);
const slicedTomatoInABowl = getBowlOf('tomato').flatMap(slice);
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);
const slicedPepperInABowl = getBowlOf('pepper').flatMap(slice);
const mixInABowl = getBowlOf('pepper') .flatMap(slice) .faltMap(silcedPepper => mix(silcedPepper , slicedPepperInABowl.get()));
const slicedPepperInABowl = getBowlOf('pepper').flatMap(slice);
const mixInABowl = getBowlOf('pepper') .flatMap(slice) .flatMap(silcedPepper => slicedPepperInABowl .flatMap(mixedTomato => mix(silcedPepper , mixedTomato)));
const mixInABowl = getBowlOf('pepper') .flatMap(slice) .faltMap(silcedPepper => getBowlOf('tomato') .flatMap(slice) .flatMap(mixedTomato => mix(silcedPepper , mixedTomato)));
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);
Practice: Maybe
UNIT
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)
BIND
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;
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;
const spiceMix = getSpice('salt').filter(isPowdered)
.flatMap(salt =>
getSpice('coriander').map(crush)
.flatMap(coriander =>
mixSpice(salt , coriadner)))
.orJust(vegeta);
But HOW ?
getSpice<V>(name: string): Maybe<V>;
crush(spice: S): S;
isPowdered(spice: S): boolean;
getSpice<V>(name: string): Maybe<V>;
crush(spice: S): S;
isPowdered(spice: S): boolean;
getSpice<V>(name: string): Maybe<V>;
crush(spice: S): S;
isPowdered(spice: S): boolean;
monet.js
Maybe
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;
}
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);
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);
const spiceMixMaybe =
getSpice('salt').filter(isPowdered).flatMap(salt =>
getSpice('coriander').map(crush).flatMap(coriander =>
mixSpice( salt , coriadner )));
const spiceMix = spiceMixMaybe.orJust(vegeta);
const spiceMixMaybe = getSpice('salt').filter(isPowdered)
.flatMap(salt =>
getSpice('coriander').map(crush)
.flatMap(coriander =>
mixSpice( salt , coriadner )));
const spiceMix = spiceMixMaybe.orJust(vegeta);
const spiceMix = getSpice('salt').filter(isPowdered)
.flatMap(salt =>
getSpice('coriander').map(crush)
.flatMap(coriander =>
mixSpice( salt , coriadner )));
.orJust(vegeta)
const spiceMix = getSpice('salt')
.filter(isPowdered)
.flatMap(salt =>
getSpice('coriander')
.map(crush)
.flatMap(coriander =>
mixSpice(salt , coriadner)))
.orJust(vegeta);
null
getSpice<V>(name: string): Maybe<V> { let spice = locker.get(name); if (spice != null) { return Some(spice); } return None();}
getSpice<V>(name: string): Maybe<V> { const spice = locker.get(name); if (spice != null) { return Some(spice); } return None();}
getSpice<V>(name: string): Maybe<V> { const spice = locker.get(name); if (spice != null) { return … ? } return None();}
monet.js
Some( … )
getSpice<V>(name: string): Maybe<V> { const spice = locker.get(name); if (spice != null) { return Some(spice); } return None();}
monet.js
None()
getSpice<V>(name: string): Maybe<V> { const spice = locker.get(name); if (spice != null) { return Some(spice); } return None();}
monet.js
.fromNull()
getSpice<V>(name: string): Maybe<V> {
return Maybe.fromNull( locker.get(name) );
}
goo.gl/cvGVeo
const saladBowl = mixInABowl
.flatMap(mixedVegetables =>
mix(mixedVegetables, spiceMix));
e a t i t
Monads
IdentityMaybeEither
ValidationListNEL
ReaderFreeIO
?
Monads
IdentityMaybeEither
ValidationListNEL
ReaderFreeIO
Promise
T H X