评论者:amalloy
Stuart:你给出的两个 foo 形式是完全分开的,要将这两者统一,你必须将它们组合在一起。用户想要一次性定义 foo,对其修改,然后再重新定义 - Clojure.core 类似地使用 let、reduce 等。
按原样编写的 deftype/reify 具有某种相似的“外观”,因为没有任何实际上将 Map 的声明与其函数分组的东西,当给出像 Map 这样的标题两次时,不清楚会发生什么,文档中也没有说明。
我认为区别在于,在 reify 的情况下,这两者在同一顶级形式中,因此编译器可以检测到你正在尝试做“奇怪”的事情,所以对于你的 defn 示例,静默重新定义(对你的示例来说是合理的)是令人惊讶的。有几种解决方案可以减少这种惊讶。
1) 允许或要求 reify 组合事物,如 (reify (Comparable (compare (link: this other) 1)))。 Comparable 与其方法的显式分组有两个目的:它暗示其他 Comparable 定义应包含在该分组中;并且因为你可以简单地迭代到 Comparable,然后插入另一个定义,所以它使这样做更容易。
2) 如果指定了两次接口,则抛出异常。这不是最佳方案,因为用户自己分组可能会很繁琐,而由于 deftype 已经在进行分组,所以很容易做到。然而,这可以避免混乱和惊讶,因为它会明确表示“不允许”,而不是让用户猜测出了什么问题。
3) 将我的原始示例代码解释为尝试打开 Map 接口,添加实现,然后稍后添加更多实现。
我希望 reify 最初就能实现 (1),但此时我认为这与语法向后兼容性不兼容,因此不是一个好主意。我想 (2) 或 (3) 都是可行的,并且它们都似乎是比当前混乱行为更好的改进。当然,我更喜欢 (3),但我可以理解有可能使 reify 拒绝意图不明确的语法,而不是将其解释为我认为最有用的意图。