【问题标题】:Should agents only hold immutable values代理是否应该只持有不可变的值
【发布时间】:2013-06-07 10:18:49
【问题描述】:

在 Clojure Programming (OReilly) 中有一个示例,其中 java.io.BufferedWriter 和 java.io.Printwriter 都放在一个代理中(每个代理一个)。然后将这些写入代理操作内部。这本书说在代理动作中执行 io 是安全的。据我了解,代理操作中的所有副作用操作都可以。这是因为只有在提交成功时才会运行提交中的代理操作。其他代理操作中的代理操作仅在外部代理操作成功完成后运行。一般而言,代理动作保证是连续应用的。

Clojure documentation 说:“代理的状态本身应该是不可变的......”。

据我了解,atoms 和 refs 必须保持不可变值的原因是 clojure 可以回滚并重试多次提交。

我不明白的是:

1:如果 Clojure 确保代理操作只运行一次,为什么代理值必须是不可变的。 (例如,如果我在代理中保存一个 java 数组,并在代理操作中添加到它,这应该没问题,因为该操作只会运行一次。这与向 BufferedWriter 添加行非常相似)

2: java.io.BufferedWriter 被认为是不可变的吗?我知道你可以有一个稳定的引用,但如果代理操作正在对其执行 io,它是否仍应被视为不可变?

3:如果BufferedWriter被认为是不可变的,如何判断其他类似的java类是否不可变?

【问题讨论】:

    标签: concurrency clojure


    【解决方案1】:

    在我看来:

    代理持有的值应该是“有效不可变的”(从 JCIP 借用的术语),因为它们在概念上应该始终与它们自己相等。

    这意味着,如果我 .clone() 一个对象并比较两个副本,original.equals(copy) 应该为真,无论我做什么(以及何时)。

    从这个意义上说,一个典型的Employee类充满getter/setter的实例不能保证等于它自己,面对可变性:equals()将被定义为逐字段比较,所以测试可能会失败。

    虽然 BufferedWriter 并不代表一个值 - 它的相等性是根据内存中完全相同的对象来定义的。所以它有一个“健全”的可变性——不像 Employee 的——这使得它很适合将它包装在一个代理中。

    我相信你是对的,从 STM 的角度来看,agent-value 的可变性不会造成太大的伤害。但它会打破 Clojure 的时间模型,在该模型中你“无法改变过去”等等。

    关于确定 Java 类是否是不可变的:不深入实现是不可能的。不过你不必太在意这个。

    我会在 Java 领域对类型进行以下分类:

    • (严重)表示值的可变对象 - Employee 等。切勿将它们包装在 Clojure 引用类型中。

    • 表示值的不可变对象 - 它们的不可变性反映在文档或命名约定(“EmployeeBuilder”)中。 可以安全地包装在任何 Clojure 引用中。

    • 非托管集合类型 - ArrayList 等。避免互操作。

    • 托管引用/集合类型 - AtomicReference、阻塞队列...它们与 Clojure 配合得很好,但怀疑将它们包装在 Clojure 引用中。

    • 'IO' 类型 - BufferedWriter、Swing 的东西......你不关心它们的可变性,因为它们根本不代表值 - 你只想要它们的副作用。 在代理中保护它们以保证访问序列化可能是有意义的。

    【讨论】:

      【解决方案2】:

      代理值应该是不可变的,因为有人可以这样做:

       (def my-agent (agent (BufferedWriter.)))
       (.write @my-agent "Hello world")
      

      这基本上是在不通过代理控制机制的情况下修改代理值(在本例中为编写器)。

      是的,BufferedWriter 是可变的,因为通过写入它你可以改变它的内部状态。它就像一个指针或引用,而不是 value.

      【讨论】:

      • 虽然没有人使用 BufferedWriter 作为引用或集合类型 - 改变它们更像是一种副作用,而不是表示程序中的变化。
      • BufferedWriter 可以写入任何底层流,甚至可以是内存流,因此可以是状态
      • 我想是的,严格来说:) 我觉得非常重要:1)区分可变性的种类,2)Clojure 作为一种动态类型的语言,将某些责任委托给了程序员。不写(.write @my-agent "Hello world")(一般情况下)这样的代码就是其中之一。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-05
      • 1970-01-01
      相关资源
      最近更新 更多