Skip to content

Instantly share code, notes, and snippets.

@blischalk
Created December 18, 2015 20:43
Show Gist options
  • Save blischalk/7bf08634d543ddd4b040 to your computer and use it in GitHub Desktop.
Save blischalk/7bf08634d543ddd4b040 to your computer and use it in GitHub Desktop.
Monad Gist
;; Lets say you have a tight rope walker Pierre and he carries a pole
;; As birds land on the pole on one side or the other it gets
;; hard for the tight rope walker to keep his balance
;; If there is more than a 4 bird difference between the left
;; and right side of the pole he will fall off the tightrope
(def pole [0 0])
(def threshold 4)
(defn abs [n] (max n (- n)))
(defn land-left [n [l r]]
(let [new-left (+ n l)]
(if (< (abs (- new-left r)) threshold)
[new-left r]
nil)))
(defn land-right [n [l r]]
(let [new-right (+ n r)]
(if (< (abs (- new-right l)) threshold)
[l new-right]
nil)))
;; This scenario will be ok because Pierre's pole is always within tolerence
(->> pole
(land-left 2)
(land-right 2)
(land-left 3)
(land-right 3)) ;; [5 5]
;; In this scenario what we want to happen is to just nicely return nil
;; letting us know that our friend Pierre has lost his balance and
;; tumbled. Alas that is not what will happen
(->> pole
(land-left 2)
(land-right 2)
(land-left 10) ;; BOOM! java.lang.NullPointerException
(land-right 3)) ;; Wanted to see nil here
;; Utilizing the Maybe Monad you would be able define a value in
;; a context of possible failure
;; Here we do our best with an extra nil check within our functions
(defn land-left' [n maybe]
(if (nil? maybe) nil
(let [[l r] maybe
new-left (+ n l)]
(if (< (abs (- new-left r)) threshold)
[new-left r]
nil))))
(defn land-right' [n maybe]
(if (nil? maybe) nil
(let [[l r] maybe
new-right (+ n r)]
(if (< (abs (- new-right l)) threshold)
[l new-right]
nil))))
(->> pole
(land-left' 2)
(land-right' 2)
(land-left' 10) ;; Works fine now
(land-right' 3)) ;; nil
;; With a type system and Monads the concept of a context of possible failure
;; can be encapsulated. Notice the below functions don't check for nil
;; Haskell example:
landLeft :: Birds -> Pole -> Maybe Pole
landLeft n (left,right)
| abs ((left + n) - right) < 4 = Just (left + n, right)
| otherwise = Nothing
landRight :: Birds -> Pole -> Maybe Pole
landRight n (left,right)
| abs (left - (right + n)) < 4 = Just (left, right + n)
| otherwise = Nothing
;; The below implemtation produces "Nothing" and does not throw an exception
return (0,0) >>= landLeft 1 >>= landRight 4 >>= landLeft (-1) >>= landRight (-2)
;; The bind operator >>= takes a value that is wrapped in a context (such as
;; the context of possible failure) and is implemented for the Maybe type
;; as such
instance Monad Maybe where
(Just x) >>= k = k x
Nothing >>= _ = Nothing
;; E.g if the context has a value in it return the result of applying the passed
;; in function to that value
;; Otherwise just return Nothing
;; Attempting a loose interpretation in Clojure
;; The bind operator here doesn't actually extract any value
;; out of a context but it does provide a similar functionality
(defn >>= [context f]
(if (nil? context) nil
(f context)))
(defn land-left'' [n pole]
(let [[l r] pole
new-left (+ n l)]
(if (< (abs (- new-left r)) threshold)
[new-left r]
nil)))
(defn land-right'' [n pole]
(let [[l r] pole
new-right (+ n r)]
(if (< (abs (- new-right l)) threshold)
[l new-right]
nil)))
(-> pole
(>>= (partial land-left'' 2))
(>>= (partial land-right'' 2))
(>>= (partial land-left'' 10))
(>>= (partial land-right'' 3)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment