评论者: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拒绝意图不明显的语法,而不是将其解释为我认为最有用的意图。