Skip to content

Instantly share code, notes, and snippets.

@jcouyang
Last active June 3, 2016 15:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcouyang/aa38b9735da1b146458026f08d954915 to your computer and use it in GitHub Desktop.
Save jcouyang/aa38b9735da1b146458026f08d954915 to your computer and use it in GitHub Desktop.
Practical Monads

Practical Monads

ToC

  • Monad
  • Promise
  • Observable
  • Demo

Monad

https://blog.oyanglul.us/javascript/images/summarize%20in%20one%20word.gif

https://blog.oyanglul.us/javascript/images/what.gif

Before Monad

ES6 101

EcmaScript5

function(x){return x+1}

EcmaScript6

x=>x+1

Haskell 101

--  HM(Hindley-Milner) type signature
liftM :: (Monad m) => (a -> b) -> m a -> m b 
--        ^type restrict ^function    ^  ^return
--                              ^curry^   

Functional 101

[1,2,3].map(x=>x+1)
// => [2,3,4]
[1,2,3].filter(x=>x/2==0)
// => [2]
[1,2,3].reduce((acc,n)=>acc+n)
// => 6

Functional 102

  1. Method -> Function
  2. First Class Function
let toUpperCase = x => x.toUpperCase()  // <- 1
let map = (f, col) => col.map(f) // <- 1
map(toUpperCase, ["hello", "world"])
//    ^2
//=> ["HELLO", "WORLD"]

Some instances of Functor, Monoid

a Array Functor

// map:: (String s) => (s -> s) -> [s] -> [s]
map(toUpperCase, ["hello", "world"])

functor is mapable!

a String Monoid

"hello" + "world" // <-- concat
// => "helloworld"
"hello" + ""   // <--  identity
// => "hello"
("hello" + "world") + "!" == "hello" + ("world" + "!") // <-- associative
// => true

Monoid is concatable!

  • Functor is mapable!
  • Monoid is concatable!

Substitution

A monad is just a monoid in the category of endofunctors, what’s the problem?

  • Catergory -> “Type”
  • Functor -> “Mapable”
  • Monoid -> “Concatable”

“A monad is just a concatable in Type of mapable, what’s the problem?”

Concatable in Type

concat

// concat :: (Monad m) => m m x -> m x
[1].concat([2]) -> [1,2]
// concat Array Array -> Array
T concat T -> T
type G[X] = T[T[X]]
G = T compose T = T.T
T.T[X] = G[X] = T[T[X]]
concat(T.T).T == T.concat(T.T)

u -> concat

  • u -> concat
  • T(u) -> lift(flat)

https://upload.wikimedia.org/wikipedia/commons/thumb/2/2b/Monad_multiplication_explicit.svg/600px-Monad_multiplication_explicit.svg.png

pure

https://blog.oyanglul.us/javascript/images/zoidberg-die.gif

pure(T[X]) == T[T[X]]

n -> pure

  • n -> pure
  • T(n) -> lift(pure)

https://upload.wikimedia.org/wikipedia/commons/thumb/5/5a/Monad_unit_explicit.svg/568px-Monad_unit_explicit.svg.png

https://drboolean.gitbooks.io/mostly-adequate-guide/content/images/onion.png

Promise

lift

liftM :: (Monad m) => (a -> b) -> m a -> m b 
const when = require('when')
// readJSON :: (Promise p, String s, Object o) => (s -> o) -> p s-> p o
let readJSON = when.lift(JSON.parse)
readJSON(when('{hello: "world"}'))
    .then(x=>console.log(x))
    .catch(e=>console.error(e.message));
// => Unexpected token h in JSON at position 1

try

https://blog.oyanglul.us/javascript/images/came-out.gif

or simply try something and wrap result in Promise

try(JSON.parse, '{hello: "world"}')
      .then(x=>console.log(x))
      .catch(e=>console.error(e.message));
// => Unexpected token h in JSON at position 1

fold

https://blog.oyanglul.us/javascript/images/method-stack.gif

foldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
// reduce :: (Promise p, Number n, Object o) => [n] -> (o -> n -> p o) -> o -> p o
when.reduce([1,2,3], (acc, n) => rest(URL + n).then(o=>merge(acc, o)), 0)

callback hell

var allRes = {}
$.get('url1', (res1) =>
      $.get('url2', (res2) =>
            $.get('url3', (res3) =>
                  allRes = merge(res1, res2, res3))
           )
     )

flatMap

let futureWorld = new Promise(resolve=>{
    setTimeout(()=>resolve("world"), 1000)
})

futureWorld
    .then(world=>new Promise(resolve=>{
        setTimeout(()=>resolve("hello"+world), 1000)
    }))
    .then(x=>console.log(x))
// 2secs later => helloworld
  • first then is flatMap
  • second then is map

Observable

https://blog.oyanglul.us/javascript/images/shit-bricks.gif

Space

imperative

var acc = 0
for(var n of [1,2,3,4]) {
    acc +=n
}

functional

[1,2,3,4].reduce((acc,x)=>acc+x)

imperative accumulate value on time

var acc = 0;
$('input').onChange(_=>acc+=_)

Space -> Time

https://blog.oyanglul.us/javascript/images/interstellar.png

FRP(Functional Reactive Programming)

Single ItemMultiple Items
synchronousgetData():TgetData():List[T]
asynchronousgetData():Future[T]getData():Observable[T]

http://reactivex.io/assets/operators/legend.png

http://reactivex.io/documentation/operators/images/just.c.png

fold

http://reactivex.io/documentation/operators/images/reduceSeed.png

flatMap

http://reactivex.io/documentation/operators/images/flatMap.c.png

fromEvent

http://reactivex.io/documentation/operators/images/fromEvent.png

https://blog.oyanglul.us/javascript/images/neat.gif

JS Bin on jsbin.com

Fin

https://blog.oyanglul.us/javascript/images/applause.jpg

Thanks

One more thing

We are hiring

@MilhouseVanHouten

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment