haw*_*eye 7 monads clojure reader-monad
我看过algo.monads和fluokitten文档.我还阅读了Jim Duey,Konrad Hinsen和Leonardo Borges的 monad博客文章.
我可以在Clojure中找到读者Monad的唯一参考是谷歌小组的讨论.
我的问题是:是否可以在Clojure中使用Haskell的Reader Monad?你能提供一个例子吗?
当然.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)
有了这些功能,我们可以定义Reader
有algo.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实现,因此我们将继续使用它.
ask
而asks
让我们看看环境.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
.