2024年Clojure状态调查中分享您的想法!

欢迎!有关本站如何运作的更多信息,请参阅关于页面

0
Clojure

reify、deftype等,如果你两次指定一个类,则会静默失败

(macroexpand '(reify Map (size (link: this) 0),

                 Counted (count (link: this) 0), 
                 Map (keySet (link: this) nil)))

;=> (reify* (link: Counted Map) (count (link: this) 0) (keySet (link: this) nil))

后面的Map部分完全替代了前面的部分,我是当我写了一个宏,为你注入一些自动的方法体时发现的。

我附上了修复版,以便以上可以展成预期的(链接:对我来说是这样的)输出。

4 个答案

0

stu发表的评论:

在Clojure中,通常重新定义某个东西会替换原有的,而不是通过合并新旧内容来增强它。这使得局部推理代码的工作方式变得容易。有人可能会认为下面的代码片段也有你描述的同种问题

(defn foo [a] 1) (defn foo [a b] 1) (foo 1) ; 第一个形式是否丢失了是一个bug吗?

我还想知道当前行为是否可能是某些宏的便利性。 (显然你的不是!)我将更改票据的类型和标题,以更好地反映请求的本质,并查看BDFL怎么说。

0

richhickey发表的评论:

需要在票据中用更多的精确性来说明一切。我不知道具体是什么问题,也不知道提出的解决方案是什么。

需要注意的是,必须接受在基接口下的定义,因此类区域不是严格的,也不期望是完整的。

0
by

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

0
by
参考: https://clojure.atlassian.net/browse/CLJ-821(由amalloy报告)
...