Monads in Coffeescript

What's this?

A crazy experiment.


The Goal

A simple syntax for monads in Coffeescript. 


... and for me to understand them better.

So, what's a monad?

"In functional programming, a monad is a structure that represents computations defined as sequences of steps."

Core Functions

From the Monad typeclass in Haskell.

class Monad m where
    (>>=) :: m a -> (a -> m b) -> m b
    return :: a -> m a

return

Takes a value and returns a monadic value.

bind / >>=

Takes a monadic value and a function which is applied to the non-monadic value.

All Clear?



Defining Maybe

Container Types

class None
    constructor: () ->
none = new None

class Some
    constructor: (@value) ->
some = (x) -> new Some x

Monad Definition

Maybe =
    return: (x) -> some x 
    bind: (x, f) -> 
        if x instanceof None then return x 
        if x instanceof Some then return (f x.value) 
        throw "#{x} is not an instance of the Maybe monad"

Defining Promise

Q

Lets use Q for our Promise monad.

Monad Definition

Promise =
    return: Q
    bind: (x, f) -> x.then f

The M Library

My small set of functions for working with monads.

M.chain

  • Takes a monad definition and one or more computations.
  • A computation must return a monadic value.
  • All computations except the first one gets applied to the non-monadic value from the previous computation.

Example

item = M.chain Maybe, getUserInput, createTodoItem, save
# If getUserInput returns None then createTodoItem and save will never be called.

M.do

  • Takes a monad definition and an object of one or more computations.
  • A computation must return a monadic value.
  • A computation can refer to non-monadic values of preceding computations through this
  • A computation cannot refer to non-monadic values of  subsequent computations.

Example with Promise

M.do Promise, 
    someDude: () -> getUser 'some.dude'
    mine: () -> getFriendsOf loggedInUser
    his: () -> getFriendsOf this.someDude
    _: () ->
        inCommon = _.intersection this.mine, this.his
        asString = inCommon.join ', '
        log "#{inCommon.length} friends in common: #{asString}."

On GitHub

github.com/OskarWickstrom/coffee-monads
Made with Slides.com