Last active
June 15, 2023 14:46
-
-
Save mauricioszabo/2e688403f408020e24e6bbdf844c0535 to your computer and use it in GitHub Desktop.
Monads with Clojure
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (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