Is it possible to make Haskell's Reader Monad in Clojure?

I looked at algo.monads and fluokitten . I also read monad blog entries by Jim Duey , Konrad Hinsen, and Leonardo Borges .

The only link I can find in Reader Monad in Clojure is a discussion of google groups .

My question is: Is it possible to make Haskell's Reader Monad in Clojure? . Could you give an example?

+7
clojure monads
source share
2 answers

Of course. A Reader is just a function that takes an environment and extracts some value from it.

With Reader , m-result takes some value and creates a reader that ignores the environment and returns that value:

 (defn reader-result [value] "Ignores environment, returns value" (fn [env] value)) 

m-bind takes the reader and the function f , which takes a value and creates a new reader. He then combines these arguments to create a new reader, which applies the original reader to the medium, passes the value that he creates to f to create a new reader, then applies this reader to the medium:

 (defn reader-bind [reader f] "Applies reader to environment, then applies f to new environment" (fn [env] (let [read-value (reader env)] ((f read-value) env)))) 

Using these functions, we can define Reader using algo.monads :

 (m/defmonad Reader [m-result reader-result m-bind reader-bind]) 

There are several important helper functions. run-reader takes the reader and environment and applies the reader to this environment:

 (defn run-reader "Runs a reader against an environment, returns the resulting environment" [reader env] (reader env)) 

Since our readers are just functions, a run-reader not strictly necessary. However, this can make everything clearer, and it brings us closer to the Haskell implementation, so we will use it in the future.

ask and asks consider the environment. ask is a reader that returns an environment. asks accepts a selector and creates a reader that applies this selector to the environment:

 (defn ask "A reader that returns the environment" [env] env) (defn asks "A reader that returns the result of f applied to the environment" [f] (fn [env] (f env))) 

This takes us far enough to go through the first Reader example :

 (defn lookup-var [name bindings] (get bindings name)) (def calc-is-count-correct? (m/domonad Reader [binding-count (asks #(lookup-var "count" %)) bindings ask] (= binding-count (count bindings)))) (defn is-count-correct? [bindings] (run-reader calc-is-count-correct? bindings)) (def sample-bindings {"count" 3, "1" 1, "b" 2}) (println (str "Count is correct for bindings " sample-bindings ": " (is-count-correct? sample-bindings))) 

Another important Reader feature is local . This requires a function that changes the environment and the reader and creates a new reader that modifies the environment before passing it to the original reader:

 (defn local [modify reader] "A reader that modifies the environment before calling the original reader" (fn [env] (run-reader reader (modify env)))) 

With this, we can move on to the second example :

 (def calc-content-len (m/domonad Reader [content ask] (count content))) (def calc-modified-content-len (local #(str "Prefix " %) calc-content-len)) (let [s "12345" modified-len (run-reader calc-modified-content-len s) len (run-reader calc-content-len s)] (println (str "Modified 's' length: " modified-len)) (println (str "Original 's' length: " len))) 

So, all that is required to make a Reader .

+7
source share

There are some fantastic examples of the following monads in Clojure here :

  • reader monad in clojure
  • writer monad in clojure
  • state monad in clojure
  • identical monad in clojure
  • possibly monad in clojure
  • either monad in clojure
+1
source share

All Articles