Skip to content

Instantly share code, notes, and snippets.

@mauricioszabo
Last active June 15, 2023 14:46
Show Gist options
  • Select an option

  • Save mauricioszabo/2e688403f408020e24e6bbdf844c0535 to your computer and use it in GitHub Desktop.

Select an option

Save mauricioszabo/2e688403f408020e24e6bbdf844c0535 to your computer and use it in GitHub Desktop.
Monads with Clojure
(ns monads)
(defn- ensure-instance [class val constructor]
(if (instance? class val)
val
(constructor val)))
(defprotocol IMonad
(bind [this f]))
; Maybe Monad
(defrecord Just [val]
IMonad
(bind [_ f] (ensure-instance Just (f val) ->Just)))
(defrecord Nothing []
IMonad
(bind [this _] this))
; Try Monad
(defrecord Failure [e]
IMonad
(bind [this _] this))
(defrecord Success [val]
IMonad
(bind [_ f] (try
(ensure-instance Success (f val) ->Success)
(catch Exception e (Failure. e)))))
; You can run monads with the following:
(bind (Success. 10) #(/ % 0))
; But, when one monad depends on the other, this could
; become VERY confusing:
(bind (Just. 10) (fn [ten]
(bind (Just. 11) (fn [eleven]
(+ ten eleven)))))
; we can work-arount this with some helper macro:
(defn- for-aux [[[bind var] & rest] fns]
(if bind
`(bind ~var (fn [~bind]
~(for-aux rest fns)))
fns))
(defmacro for-monad [binds body]
(assert (even? (count binds)) "Binds must be even")
(for-aux (partition 2 binds) body))
; It works like this:
; (NOTE: This is equivalent to Scala's code
; for {
; ten <- Some(10)
; eleven <- Some(11)
; } yield ten + eleven
(for-monad [ten (Just. 10)
eleven (Just. 11)]
(+ ten eleven))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment