什么是Clojure关闭记录背后的理由?

时间:2010-08-28 05:14:12

标签: clojure

我可以选择在defrecord的主体中直接实现协议,而不是使用extend-protocol / extend-type

(defprotocol Fly
  (fly [this]))

(defrecord Bird [name]
  Fly
  (fly [this] (format "%s flies" name)))

=>(fly (Bird. "crow"))
"crow flies"

如果我现在尝试覆盖Fly协议,我会收到错误

(extend-type Bird
  Fly
  (fly [this] (format "%s flies away (:name this)))

class user.Bird already directly implements interface user.Fly for protocol:#'user/Fly

另一方面,如果我最初使用extend-type

(defrecord Dragon [color])

(extend-type Dragon
  Fly
  (fly [this] (format "%s dragon flies" (:color this))))

=>(fly (Dragon. "Red"))
"Red dragon flies"

然后我可以“覆盖”飞行功能

(extend-type Dragon
  Fly
  (fly [this] (format "%s dragon flies away" (:color this))))

=>(fly (Dragon. "Blue"))
"Blue dragon flies away"

我的问题是,为什么不在这两种情况下都允许延期?这是一个JVM限制,因为Record< - >类关系还是非覆盖协议的用例?

1 个答案:

答案 0 :(得分:26)

在一个层面上,JVM的一个问题是不允许将方法实现交换进出类,因为实现内联协议相当于让defrecord创建的类实现了与之对应的接口。协议。请注意,虽然选择这样做确实会牺牲一些灵活性,但它也会提高速度 - 事实上,速度是这里的主要设计考虑因素。

在另一个层面上,对于由不具有相关类型或协议的代码提供的类型的协议实现,通常是非常糟糕的想法。请参阅Clojure Google小组上的this thread进行相关讨论(包括Rich Hickey的声明); Clojure Library Coding Standards中还有一个相关条目:

  

协议:

     
      
  • 如果他拥有任何一个协议,应该只将协议扩展到一个类型   类型或协议。
  •   
  • 如果违反以前的规则,他应该准备退出,   是否应该是实施者   提供定义
  •   
  • 如果协议附带Clojure本身,请避免将其扩展到类型   你不拥有,特别是   java.lang.String和其他核心Java   接口。如果一个协议,请放心   应该扩展到它,否则   大厅吧。      
        
    • 正如Rich Hickey所说,动机是[以防止]“人   将协议扩展到其类型   它们没有意义,例如为此   协议作者考虑但是   因a而拒绝执行   语义不匹配。“。”没有扩展名   会在那里(按设计)和人   没有足够的   理解/技能可能会填补   没有破碎的想法。“
    •   
  •   

在Haskell社区中已经对类型类(谷歌“孤儿实例”)进行了很多讨论;这里也有一些关于这个主题的好帖子。

现在很明显,内联实现总是由类型的所有者提供,因此不应该被客户端代码替换。因此,我可以看到两个有效的用例仍然用于协议方法替换:

  1. 在REPL上测试并应用快速调整;

  2. 修改正在运行的Clojure图像中的协议方法实现。

  3. (1)如果使用extend&公司在开发时只在一些后期性能调整阶段切换到内联实现; (2)如果需要最高速度,则可能需要牺牲一些东西。