Clojure更改update-in返回nil和dosync不允许重复

时间:2016-10-05 16:10:34

标签: clojure hashmap apply stm update-in

编辑:

dosync创建一个函数,因此对recur的调用将被解释为对生成的函数dosync的调用。

这是我实际制作的功能的足迹。我认为保持尽可能简单。

(defn change-to-ref [ref data]
  (dosync
    (if-let [[new-ref new-data] (some-test @ref data)]
      (recur new-ref new-data)
      (alter ref f data))))

例外:

CompilerException java.lang.IllegalArgumentException:
Mismatched argument count to recur, expected: 0 args, got: 2

ORIGINAL:

我试图更新ref中的hashmap,如下所示

(def example-ref (ref {:some {:nested {:structure}}}))
(defn f [this] [this])  ;; just an example function

(sync (alter example-ref update-in [:some] f)

user=> nil

因为它应该返回

,这是非常令人惊讶的
user=> {:some [{:nested {:structure}}]}

比我尝试过:

(update-in @example-ref [:some] f)

user=> {:some [{:nested {:structure}}]}

但是,我读到alter调用apply所以:

(apply update-in @example-ref '([:some] f))

user=> {:some nil}

好的,让我们以正确的方式做到:

(apply update-in @example-ref (list [:some] f))

user=> {:some [{:nested {:structure}}]}

很好,我想到这一点, 但它没有解释为什么alter出错了 我甚至无法改变它......

(apply (fn [a b] (update-in a b f)) @example-ref '([:something]))

user=> {:some [{:nested {:structure}}]}

它看起来很糟糕,但至少它可以工作,我可以模拟alter:D

(sync (alter example-ref (fn [a b] (update-in a b f)) [:some])

user=> nil

好的,你赢了。

我看了看: clojure.lang.Ref.alter source 但是没有变得更聪明。 (根据我的理解,alter实际上不会调用apply

我希望你们中的一些人能够理解这一点并得到关于正确代码的答案。

1 个答案:

答案 0 :(得分:0)

问题是sync的签名如下:

  

(sync flags-ignored-for-now& body)

忽略此宏的第一个参数。此外,文档建议为其传递nil

  

transaction-flags => TBD,暂时没有通过

因此,使用sync的正确方法是:

> (sync nil (alter example-ref update-in [:some] (partial conj [])))
{:some [{:nested {:structure :foo}}]}

我还建议使用dosync代替sync(只是不要弄乱第一个参数;本质上是这些函数are the same):

> (dosync (alter example-ref update-in [:some] (partial conj [])))
{:some [{:nested {:structure :foo}}]}