以下Clojure代码中的alter能否用commute替换?

4
(def alice-height
  (ref 3))

(def right-hand-bites
  (ref 10))

(defn eat-from-right-hand []
  (dosync
    (when (pos? @right-hand-bites)
      (alter right-hand-bites dec)
      (alter alice-height #(+ % 24)))))

这段代码来自于《Living Clojure》一书。在书中,作者还给出了一个使用commute替换alter的例子。我想知道,在开头的pos?测试中,我们真的能进行这个替换吗?

1个回答

1
不,将减少“右手位”的代码从alter替换为commute是不正确的。
条件语句的意图显然是防止“右手位”变为负数。只有在假定“右手位”在事务结束前不会改变的情况下,递减才是有效的。虽然像alter一样,commute也有其自己的快照视图,但它将在提交时重新读取和重新应用ref的commute函数,并且这在此程序中是错误的。
因此,使用commute可以将负值提交给“右手位”。
要么坚持使用alter,要么使用ensure代替@(尽管这使整个commute练习变得毫无意义)。

有趣的小事实:在这里,“ensure”不仅毫无意义,而且容易发生(灾难性的)活锁,我可以使用您的代码片段轻松地再现这种情况。请参见 [CLJ-2301] (https://clojure.atlassian.net/projects/CLJ/issues/CLJ-2301)。 - glts
我认为这是否重要归结于在条件 (when (pos? @right-hand-bites) ...) 中读取的问题。 right-hand-bites 上的 deref 不在 commute 内,应在提交之前受到快照一致性的限制。因此,在读取期间如果条件看到了正数的 right-hand-bites,那么在提交之前它应该保证有一个一致的值。如果条件在 commute 内部,则情况会改变。 - A. Webb
@A.Webb 参考读取不能在提交时检测到修改 - 为此,您需要使用ensure。这里再次是问题的关键:commute在提交期间(仍处于事务中)重新读取引用的_当前值_(快照之外的值!),然后_无条件地_重新应用通勤函数。这就是它成为检查-然后-操作错误的地方。在REPL中尝试自己验证并不太困难。 - glts

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接