clojure.algo.monad使用parser-m执行奇怪的m-plus行为-为什么要计算第二个m-plus?
在我写的一些单子里,我有了意想不到的行为。 我已经用clojure.algo.monad使用parser-m执行奇怪的m-plus行为-为什么要计算第二个m-plus?,clojure,monads,state-monad,Clojure,Monads,State Monad,在我写的一些单子里,我有了意想不到的行为。 我已经用 (def parser-m (state-t maybe-m)) 这几乎就是到处给出的例子(,和) 我使用m-plus作为一种直通式查询机制,在我的例子中,它首先从缓存(数据库)读取值,如果返回nil,下一个方法是从“live”(REST调用)读取 但是,始终调用m-plus列表中的第二个值,即使它的值已被忽略(如果缓存命中率良好),并且最终返回的是第一个一元函数的值 这是我看到的问题的简化版本,以及我找到的一些解决方案,但我不知道为什么
(def parser-m (state-t maybe-m))
这几乎就是到处给出的例子(,和)
我使用m-plus作为一种直通式查询机制,在我的例子中,它首先从缓存(数据库)读取值,如果返回nil,下一个方法是从“live”(REST调用)读取
但是,始终调用m-plus列表中的第二个值,即使它的值已被忽略(如果缓存命中率良好),并且最终返回的是第一个一元函数的值
这是我看到的问题的简化版本,以及我找到的一些解决方案,但我不知道为什么
我的问题是:
(获取状态)
从checker,当我评估该方法时,它
打印m-plus正在调用的函数的消息
(当我认为不应该的时候)。这也是一个bug吗(ns monads.monad-test
(:require [clojure.algo.monads :refer :all]))
(def parser-m (state-t maybe-m))
(defn check-k-v [k v]
(println "calling with k,v:" k v)
(domonad parser-m
[kv (fetch-val k)
_ (do (println "k v kv (= kv v)" k v kv (= kv v)) (m-result 0))
:when (= kv v)
_ (do (println "passed") (m-result 0))
_ (update-val :ran #(conj % (str "[" k " = " v "]")))
]
[k v]))
(defn filler []
(println "filler called")
(domonad parser-m
[_ (fetch-state)
_ (do (println "filling") (m-result 0))
:when nil]
nil))
(def checker
(domonad parser-m
[_ (fetch-state)
result (m-plus
;; (filler) ;; intitially commented out deliberately
(check-k-v :a 1)
(check-k-v :b 2)
(check-k-v :c 3))]
result))
(checker {:a 1 :b 2 :c 3 :ran []})
按原样运行时,输出为:
> (checker {:a 1 :b 2 :c 3 :ran []})
calling with k,v: :a 1
calling with k,v: :b 2
calling with k,v: :c 3
k v kv (= kv v) :a 1 1 true
passed
k v kv (= kv v) :b 2 2 true
passed
[[:a 1] {:a 1, :b 2, :c 3, :ran ["[:a = 1]"]}]
我不希望线路kvkv(=kv):b22 true
显示出来。最终的结果是从第一个函数返回给m-plus的值,正如我所期望的,但我甚至不期望调用第二个函数
现在,我发现如果我将一个填充符传递到m-plus中,而该填充符不做任何操作(即取消注释(filler)
行),那么输出是正确的,不会计算:b值
如果我没有filler方法,并且使第一个方法测试失败(即,将其更改为(check-k-v:a2)
,那么一切都很好,我没有接到check:c的调用,只测试了a和b
根据我对state-t-maybe-m
转换的理解,m-plus函数应该如下所示:
(defn m-plus
[left right]
(fn [state]
(if-let [result (left state)]
result
(right state))))
这意味着除非left
返回nil/false,否则不会调用right
编辑:
在查看state-t和maybe-m源之后,m-plus看起来更像:
(fn [& statements]
(fn [state]
(apply (fn [& xs]
(first (drop-while nil? xs)))
(map #(% state) statements))))
但原理是一样的,(首先(在为零时删除?…)
应该只在返回有效值的项上执行
我很想知道我的理解是否正确,以及为什么我必须使用filler方法来停止额外的评估(我不希望发生这种情况)
编辑:
如果我切换到使用(从他优秀的博客中),m-plus中的第二个函数没有评估,这似乎意味着转换monad正在破坏m-plus。然而,即使在这个实现中,如果我删除初始的(获取状态)
调用checker
函数时,domonad定义会导致创建m-plus函数的输出,这表明domonad的实现中发生了我不期望的事情
为这篇冗长的文章道歉!在最近的一篇文章之后,我怀疑问题在于应用程序读取了大量参数以确定函数的算术性,导致它调用了一个额外的方法。