Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Awesomely descriptive JavaScript
with 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 E
A
L
U
E
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
function
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( mona...
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)
mon...
Monad laws (monet.js / Identity)
Identity(v).flatMap(f) ≡ f(v)
id.flatMap(Identity) ≡ id
id.flatMap(f).flatMap( g ) ≡
id.f...
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)
mon...
var pepperInABowl = getBowlOf('pepper', 'red');
var pepper = pepperInABowl.get();
var slicedPepperInABowl = slice(pepper);...
const mixInABowl = getBowlOf('pepper')
.flatMap(slice)
.faltMap(silcedPepper => getBowlOf('tomato')
.flatMap(slice)
.faltM...
But HOW ?
interface Bowl<V> {
get(): V;
}
getBowlOf<V>(name: string): Bowl<V>;
slice<S>(vegetable: Vege): Bowl<S>;
mix<M>(...ingredi...
var pepperInABowl = getBowlOf('pepper');
var pepper = pepperInABowl.get();
var slicedPepperInABowl = slice(pepper);
var to...
var pepperInABowl = getBowlOf('pepper');
var pepper = pepperInABowl.get();
var slicedPepperInABowl = slice(pepper);
var to...
var pepperInABowl = getBowlOf('pepper');
var pepper = pepperInABowl.get();
var slicedPepperInABowl = slice(pepper);
var to...
var pepperInABowl = getBowlOf('pepper');
var pepper = pepperInABowl.get();
var slicedPepperInABowl = slice(pepper);
var to...
monet.js
Identity( … )
type Op<A, B> = (val:A) => Bowl<B>;
interface Bowl<V> {
get(): V;
flatMap<S>(f: Op<V, S>): Bowl<S>;
}
goo.gl/dh9DrZ
var pepperInABowl = getBowlOf('pepper');
var pepper = pepperInABowl.get();
var slicedPepperInABowl = slice(pepper);
var to...
const slicedPepperInABowl =
getBowlOf('pepper').flatMap(slice);
var pepperInABowl = getBowlOf('pepper');
var pepper = pepperInABowl.get();
var slicedPepperInABowl = slice(pepper);
var to...
const slicedPepperInABowl =
getBowlOf('pepper').flatMap(slice);
const slicedTomatoInABowl =
getBowlOf('tomato').flatMap(sl...
var pepperInABowl = getBowlOf('pepper');
var pepper = pepperInABowl.get();
var slicedPepperInABowl = slice(pepper);
var to...
const slicedPepperInABowl =
getBowlOf('pepper').flatMap(slice);
const mixInABowl = getBowlOf('pepper')
.flatMap(slice)
.fa...
const slicedPepperInABowl =
getBowlOf('pepper').flatMap(slice);
const mixInABowl = getBowlOf('pepper')
.flatMap(slice)
.fl...
const mixInABowl = getBowlOf('pepper')
.flatMap(slice)
.faltMap(silcedPepper => getBowlOf('tomato')
.flatMap(slice)
.flatM...
var pepperInABowl = getBowlOf('pepper', 'red');
var pepper = pepperInABowl.get();
var slicedPepperInABowl = slice(pepper);...
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)
mon...
let spiceMix;
const salt = getSpice('salt');
let coriander = getSpice('coriander');
if (coriander) {
coriander = crush(cor...
let spiceMix;
const salt = getSpice('salt');
let coriander = getSpice('coriander');
if (coriander) {
coriander = crush(cor...
const spiceMix = getSpice('salt').filter(isPowdered)
.flatMap(salt =>
getSpice('coriander').map(crush)
.flatMap(coriander ...
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:...
const silkySalt = getSpice('salt').filter(isPowdered);
const crushedCoriander = getSpice('coriander').map(crush);
const sp...
const crushedCoriander = getSpice('coriander').map(crush);
const spiceMixMaybe =
getSpice('salt').filter(isPowdered).flatM...
const spiceMixMaybe =
getSpice('salt').filter(isPowdered).flatMap(salt =>
getSpice('coriander').map(crush).flatMap(coriand...
const spiceMixMaybe = getSpice('salt').filter(isPowdered)
.flatMap(salt =>
getSpice('coriander').map(crush)
.flatMap(coria...
const spiceMix = getSpice('salt').filter(isPowdered)
.flatMap(salt =>
getSpice('coriander').map(crush)
.flatMap(coriander ...
const spiceMix = getSpice('salt')
.filter(isPowdered)
.flatMap(salt =>
getSpice('coriander')
.map(crush)
.flatMap(coriande...
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 No...
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 No...
monet.js
None()
getSpice<V>(name: string): Maybe<V> {
const spice = locker.get(name);
if (spice != null) {
return Some(spice);
}
return No...
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
Identity
Maybe
Either
Validation
List
NEL
Reader
Free
IO
?
Monads
Identity
Maybe
Either
Validation
List
NEL
Reader
Free
IO
Promise
T H X
Jakub . Strojewski @gmail.com
u l f r y k
Awesomely descriptive JavaScript with monads
Awesomely descriptive JavaScript with monads
Awesomely descriptive JavaScript with monads
Awesomely descriptive JavaScript with monads
Awesomely descriptive JavaScript with monads
Awesomely descriptive JavaScript with monads
Awesomely descriptive JavaScript with monads
Awesomely descriptive JavaScript with monads
Awesomely descriptive JavaScript with monads
Upcoming SlideShare
Loading in …5
×

Awesomely descriptive JavaScript with monads

423 views

Published on

"Can unfamiliar pattern from world of statically typed functional languages improve JavaScript world? For sure YES! The elastic nature of JavaScript can handle great ideas from many alien worlds. With a little help of modern tools like ​monet.js​ and ​TypeScript​ we can achieve awesomely descriptive aesthetics of our code and drop some imperative bloat."

On 19 march 2016 Kuba Strojewski from Evojam gave a speech about monads on meet.js summit JavaScript Conference. Here's his slide deck.

Published in: Software
  • Be the first to comment

Awesomely descriptive JavaScript with monads

  1. 1. Awesomely descriptive JavaScript with Monads
  2. 2. Theory
  3. 3. Monad laws return a >>= f ≡ f a m >>= return ≡ m (m >>= f) >>= g ≡ m >>= (x -> f x >>= g)
  4. 4. Monad laws
  5. 5. Practical theory
  6. 6. V E G E T A B L E A L U E
  7. 7. P => M<S> f
  8. 8. => unit P => M<P>
  9. 9. ( , ) => bind ( M<P> , P => M<S> ) => M<S>
  10. 10. ( 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
  11. 11. 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 ) )
  12. 12. monet.js
  13. 13. 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
  14. 14. 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))
  15. 15. Practice: Identity
  16. 16. 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
  17. 17. 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);
  18. 18. const mixInABowl = getBowlOf('pepper') .flatMap(slice) .faltMap(silcedPepper => getBowlOf('tomato') .flatMap(slice) .faltMap(mixedTomato => mix(silcedPepper , mixedTomato)));
  19. 19. But HOW ?
  20. 20. interface Bowl<V> { get(): V; } getBowlOf<V>(name: string): Bowl<V>; slice<S>(vegetable: Vege): Bowl<S>; mix<M>(...ingredients): Bowl<M>;
  21. 21. 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);
  22. 22. 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);
  23. 23. 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);
  24. 24. 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);
  25. 25. monet.js Identity( … )
  26. 26. type Op<A, B> = (val:A) => Bowl<B>; interface Bowl<V> { get(): V; flatMap<S>(f: Op<V, S>): Bowl<S>; }
  27. 27. goo.gl/dh9DrZ
  28. 28. 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. 29. const slicedPepperInABowl = getBowlOf('pepper').flatMap(slice);
  30. 30. 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);
  31. 31. const slicedPepperInABowl = getBowlOf('pepper').flatMap(slice); const slicedTomatoInABowl = getBowlOf('tomato').flatMap(slice);
  32. 32. 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);
  33. 33. const slicedPepperInABowl = getBowlOf('pepper').flatMap(slice); const mixInABowl = getBowlOf('pepper') .flatMap(slice) .faltMap(silcedPepper => mix(silcedPepper , slicedPepperInABowl.get()));
  34. 34. const slicedPepperInABowl = getBowlOf('pepper').flatMap(slice); const mixInABowl = getBowlOf('pepper') .flatMap(slice) .flatMap(silcedPepper => slicedPepperInABowl .flatMap(mixedTomato => mix(silcedPepper , mixedTomato)));
  35. 35. const mixInABowl = getBowlOf('pepper') .flatMap(slice) .faltMap(silcedPepper => getBowlOf('tomato') .flatMap(slice) .flatMap(mixedTomato => mix(silcedPepper , mixedTomato)));
  36. 36. 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);
  37. 37. Practice: Maybe
  38. 38. 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
  39. 39. 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;
  40. 40. 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;
  41. 41. const spiceMix = getSpice('salt').filter(isPowdered) .flatMap(salt => getSpice('coriander').map(crush) .flatMap(coriander => mixSpice(salt , coriadner))) .orJust(vegeta);
  42. 42. But HOW ?
  43. 43. getSpice<V>(name: string): Maybe<V>; crush(spice: S): S; isPowdered(spice: S): boolean;
  44. 44. getSpice<V>(name: string): Maybe<V>; crush(spice: S): S; isPowdered(spice: S): boolean;
  45. 45. getSpice<V>(name: string): Maybe<V>; crush(spice: S): S; isPowdered(spice: S): boolean;
  46. 46. monet.js Maybe
  47. 47. 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; }
  48. 48. 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);
  49. 49. 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);
  50. 50. const spiceMixMaybe = getSpice('salt').filter(isPowdered).flatMap(salt => getSpice('coriander').map(crush).flatMap(coriander => mixSpice( salt , coriadner ))); const spiceMix = spiceMixMaybe.orJust(vegeta);
  51. 51. const spiceMixMaybe = getSpice('salt').filter(isPowdered) .flatMap(salt => getSpice('coriander').map(crush) .flatMap(coriander => mixSpice( salt , coriadner ))); const spiceMix = spiceMixMaybe.orJust(vegeta);
  52. 52. const spiceMix = getSpice('salt').filter(isPowdered) .flatMap(salt => getSpice('coriander').map(crush) .flatMap(coriander => mixSpice( salt , coriadner ))); .orJust(vegeta)
  53. 53. const spiceMix = getSpice('salt') .filter(isPowdered) .flatMap(salt => getSpice('coriander') .map(crush) .flatMap(coriander => mixSpice(salt , coriadner))) .orJust(vegeta);
  54. 54. null
  55. 55. getSpice<V>(name: string): Maybe<V> { let spice = locker.get(name); if (spice != null) { return Some(spice); } return None(); }
  56. 56. getSpice<V>(name: string): Maybe<V> { const spice = locker.get(name); if (spice != null) { return Some(spice); } return None(); }
  57. 57. getSpice<V>(name: string): Maybe<V> { const spice = locker.get(name); if (spice != null) { return … ? } return None(); }
  58. 58. monet.js Some( … )
  59. 59. getSpice<V>(name: string): Maybe<V> { const spice = locker.get(name); if (spice != null) { return Some(spice); } return None(); }
  60. 60. monet.js None()
  61. 61. getSpice<V>(name: string): Maybe<V> { const spice = locker.get(name); if (spice != null) { return Some(spice); } return None(); }
  62. 62. monet.js .fromNull()
  63. 63. getSpice<V>(name: string): Maybe<V> { return Maybe.fromNull( locker.get(name) ); }
  64. 64. goo.gl/cvGVeo
  65. 65. const saladBowl = mixInABowl .flatMap(mixedVegetables => mix(mixedVegetables, spiceMix));
  66. 66. e a t i t
  67. 67. Monads Identity Maybe Either Validation List NEL Reader Free IO ?
  68. 68. Monads Identity Maybe Either Validation List NEL Reader Free IO Promise
  69. 69. T H X
  70. 70. Jakub . Strojewski @gmail.com u l f r y k

×