评论由: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拒绝那些意图不是立即明显但不包括我认为最有用的意图的语法。