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列表中的第二个值,即使它的值已被忽略(如果缓存命中率良好),并且最终返回的是第一个一元函数的值

这是我看到的问题的简化版本,以及我找到的一些解决方案,但我不知道为什么

我的问题是:

  • 这是预期的行为还是m-plus中的错误?i、 e.即使第一项返回一个值,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的实现中发生了我不期望的事情


    为这篇冗长的文章道歉!

    在最近的一篇文章之后,我怀疑问题在于应用程序读取了大量参数以确定函数的算术性,导致它调用了一个额外的方法。