是否可以在Clojure中使用Haskell的Reader Monad?

haw*_*eye 7 monads clojure reader-monad

我看过algo.monadsfluokitten文档.我还阅读了Jim Duey,Konrad HinsenLeonardo Borges的 monad博客文章.

我可以在Clojure中找到读者Monad的唯一参考是谷歌小组的讨论.

我的问题是:是否可以在Clojure中使用HaskellReader Monad你能提供一个例子吗?

Bey*_*mor 7

当然.A Reader只是一个需要环境并从中提取一些价值的函数.

使用Reader,m-result取一些值并生成一个忽略环境并返回该值的读取器:

(defn reader-result
  [value]
  "Ignores environment, returns value"
  (fn [env]
    value))
Run Code Online (Sandbox Code Playgroud)

m-bind需要一个读者和一个f接受一个值并产生一个新读者的函数.然后它将这些参数组合在一起以产生一个新的阅读器,该阅读器将初始阅读器应用于环境,将其生成的值提供f给生成新的阅读器,然后将该阅读器应用于环境:

(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))))
Run Code Online (Sandbox Code Playgroud)

有了这些功能,我们可以定义Readeralgo.monads:

(m/defmonad Reader
          [m-result  reader-result
           m-bind    reader-bind])
Run Code Online (Sandbox Code Playgroud)

有一些重要的辅助函数.run-reader需要读者和环境并将读者应用于该环境:

(defn run-reader
  "Runs a reader against an environment,
   returns the resulting environment"
  [reader env]
  (reader env))
Run Code Online (Sandbox Code Playgroud)

由于我们的读者只是功能,run-reader不是绝对必要的.但是,它可以使事情更清晰,并使我们更接近Haskell实现,因此我们将继续使用它.

askasks让我们看看环境.ask是一个返回环境的读者.asks选择一个选择器并创建一个将该选择器应用于环境的阅读器:

(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)))
Run Code Online (Sandbox Code Playgroud)

这让我们足够了解第一个Reader例子:

(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)))
Run Code Online (Sandbox Code Playgroud)

另一个重要的Reader功能是local.这需要一个修改环境和阅读器的函数,并创建一个新的阅读器,在将环境传递给原始阅读器之前修改环境:

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

有了它,我们可以通过第二个例子:

(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)))
Run Code Online (Sandbox Code Playgroud)

所以,这就是所需要的Reader.