Skip to content

Instantly share code, notes, and snippets.

@amitayh
Last active August 1, 2017 05:40
Show Gist options
  • Save amitayh/bb9c3a8348b1ebf8574c8c01ca977f27 to your computer and use it in GitHub Desktop.
Save amitayh/bb9c3a8348b1ebf8574c8c01ca977f27 to your computer and use it in GitHub Desktop.
; Abstract methods for monadic chaining
(defprotocol Chainable
(fmap [this f])
(bind [this f]))
; Helper methods for "either" monad
(defrecord Right [value])
(defrecord Left [error])
(extend-type Right
Chainable
(fmap [this f] (->Right (f (:value this))))
(bind [this f] (f (:value this))))
(extend-type Left
Chainable
(fmap [this f] this)
(bind [this f] this))
; Chaining macro
; Works similarly to Scala's for comprehension or Haskell's 'do' notation
(defmacro chain [bindings body]
(if (= (count bindings) 2)
`(fmap
~(second bindings)
(fn [~(first bindings)] ~body))
(let [first-pair (take 2 bindings)
other-pairs (vec (drop 2 bindings))]
`(bind
~(second first-pair)
(fn [~(first first-pair)]
(chain ~other-pairs ~body))))))
; Example usage, returns Right with "hello, HELLO!"
(chain [foo (->Right "hello")
bar (->Right (clojure.string/upper-case foo))
baz (->Right (str foo ", " bar))]
(str baz "!"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment