评论者:amalloy
Stuart:你给出的两个foo形式是完全独立的,要将这两种行为统一,您需要将它们分组。用户想要一次性定义foo,对它进行修改,然后重新定义它是完全合理的——clojure.core 就使用 let、reduce 等进行类似操作。
按现在的写法,deftype/reify 有一定的相似之处——因为没有任何物理上分组 Map 的声明和相关函数,所以当 Heading 类似于 Map 重复出现时,并不清楚会发生什么,而且文档中也没有说明。
我认为差异在于 reify 情况下,两者位于相同的顶级形式中,因此编译器可以检测到您正在尝试做一些“奇怪”的操作,因此 defn 示例中的无声重新定义(对于您的示例是合理的)会让人感到惊讶。有几种解决方案可以减少这种惊讶:
1) 允许或要求 reify 按组组织东西,就像 (reify (Comparable (compare (this other) 1))) 一样。这样,将 Comparable 与其方法显式分组有两个目的:它暗示了其他为 Comparable 定义的实现应该包含在那个分组中;并且使它更容易执行操作,因为您只需遍历形式,找到 Comparable,然后插入另一个定义。
2) 如果接口被指定两次,则抛出异常。这并不是最佳选择,因为用户自己分组东西可能是一件很麻烦的事情,而根据已有的分组,deftype 做起来比较容易。但是,它可以通过声明“不允许”而不是让用户猜测出了什么问题,来避免混淆和惊讶。
3) 将我的原始示例代码解释为打开 Map 接口,添加实现,然后以后再添加更多实现。
我原本希望 reify 首先实现(1),但在此阶段,我相信语法不向后兼容,因此这似乎不是一个好主意。我想,(2) 或 (3) 都可以,而且它们似乎都比当前令人困惑的行为有所改进。当然,我更倾向于(3),但我可以理解希望使 reify 拒绝具有不明显意图的语法,而不是将其解释为我认为最有用的意图。