评论人:amalloy
Stuart:你给出的两个foo形式是完全独立的,为了统一这两个行为,您需要将它们分组在一起。用户想要定义foo一次,修改它,然后再重新定义它,这并不荒谬——clojure.core使用let、reduce等做类似的事情。
按原样书写,deftype/reify有相似的“外观”——因为没有任何东西物理上将Map的声明与其函数分组在一起,当给Map这样的头两次时,不清楚会发生什么,文档中没有指定。
我想区别在于,在reify的情况下,这两个是在同一个顶层形式中,所以编译器可以检测到你正在尝试做一些“奇怪”的事情,因此,对于你的defn示例来说,意外地沉默地重新定义是令人惊讶的。有一些解决方案可以减少这种惊讶
1)允许或要求reify分组事物,如(reify(Comparable(compare(this other)1)))。然后明确将Comparable及其方法分组的服务有两个目的:它暗示其他Comparable的定义应该包含在这个分组中;并且这使得更容易做到这一点,因为你只需迭代形式直到找到Comparable,然后插入另一个定义。
2)如果指定了两次接口,就抛出一个异常。这并不是理想的,因为用户自己分组事物的分配可能会很多工作,而因为deftype已经做了分组,所以很容易。然而,它通过说“这是不允许的”而不是让用户猜测出了什么问题来避免混乱和惊讶。
3)将我的原始示例代码解释为尝试打开Map接口,添加实现,然后稍后添加一些更多实现。
我原本希望reify一开始就实现(1),但在这个时候我不认为语法是向后兼容的,所以这似乎不是一个好主意。我想,(2)或(3)都行,它们似乎都优于目前的令人困惑的行为。当然,我更喜欢(3),但我可以理解希望reify拒绝不明显表达意图的语法,而不是将其解释为我认为最有用意图的意图。