LuminousMonkey

Why is a Monad like a writing desk?

I have seen this talk, however, given that someone I know was recently asked about monads in an interview, I thought it best to link to it here so I can find it later.

What is a Monad like a writing desk?

Even if you're not interested in monads, you should watch this talk, because it tells a story. Talks are not about dense information, but are about getting people interested in the subject.

What follows are my own notes, for no other reason, but for me to follow along and try to understand.

Monadic return operation:

(defn return [v] (fn [] v))

You define a function that takes a value and wraps that value in a function. When that function is executed, it returns the value.

((return "jelly")) ;=> "jelly"

This is not a monadic return, because the value is not wrapped up in a function:

(defn return [v] v)

(return "jelly") ;=> jelly

Monadic bind operation:

(defn bind [mv f] (f (mv)))

(defn with-toast [s]
  (return (str "toast & " s)))

(bind (return "jelly") with-toast)
;=> container/monadic value (Our value wrapped in a function)

((bind (return "jelly") with-toast))
;=> "toast & jelly"

Bind will take a monadic value, and a function, calling the monadic value so it will get the actual value, and pass that in as a parameter. The function passed into bind must be a monadic value (wrapped in a function.)

Ok, I follow this, but… why?

Can you write a function that makes you grow and returns a monadic value. Sure:

(return "me")

(defn grow [s]
  (return (str s (last s))))

(grow "me") ;=> monadic value

((grow "me")) ;=> "mee"

Using bind to grow:

(defn m-grow [mv]
  (bind mv grow))

((m-grow (return "me"))) ;=> "mee"

Ok, and you can chain them… but what is the purpose? What is the benefit? Handling of nil. With modifications to bind, you can have monadic operations occur with nil, that don't blow up. Instead it could pass though a call chain, and return nil wrapped in a monadic value.

Monads are like astronauts, monads are like burritos. Yeah, what the hell are they on about?

Updating of return and bind, so they can include state:

;; Will now return a monadic value that needs a parameter.
(defn return [v]
  (fn [s] [v s]))

;; Same here
(defn bind [mv f]
  (fn [s]
    (let [[v sn] (mv s)]
      ((f v) sn))))

There was a tea function, which I haven't written down, but it's used again without modification here.

(defn m-tea [mv name]
  (bind mv (fn [v]
             (return (str v " and " name)))))

((-> (return "me") (m-tea "you")) 10)
;=> ["me and you" 10]

So, we can track state.

(defn take-sugar [mv]
  (bind mv (fn [v]
             (fn [s] [v (sec s)]))))

Seems to be all about using closures for state and composing operations?

Identity monad: return, bind.

Maybe monad: The handling of nil.

(defn bind [mv f]
  (let [v (mv)]
    (if (nil? v)
      (return nil)
      (f v))))

State monad: Tea.

Things in common:

Left Unit - "return" acts as a neutral element of bind.

(bind (return v) f) ; same as (f v)

(defn return [v] (fn [] v))
(defn bind [mv f] (f (mv)))

(defn grow [s] (return (str s (str (last s)))))

((bind (return "me") "grow")) ;=> "mee"
((grow "me"))                 ;=> "mee"

Right Unit - "return" acts as a neutral element of bind.

(bind mv return) ; same as mv

(defn return [v] (fn [] v))
(defn bind [mv f] (f (mv)))

(defn grow [s] (return (str s (str (last s)))))

((bind (return "me") return)) ;=> "me"
((return "me"))               ;=> "me"

Associative - Binding two functions in succession is the same as binding one function that can be determined from them.

(bind (bind mv f) g) ;same as (bind mv (fn [x] (bind (f x) g)))

(defn return [v] (fn [] v))
(defn bind [mv f] (f (mv)))

(defn grow [s] (return (str s (str (last s)))))

((bind (bind (return "me") grow) grow)) ;=> "meee"
((bind (return "me")
       (fn [v] (bind (grow v) grow))))  ;=> "meee"

Monads are all about keeping inside functions pure and having messy stuff on the outside.