为什么Clojure有5种定义类的方式而不是只有一种?

88

Clojure有gen-class、reify、proxy,还有deftype和defrecord等定义新类似数据类型的方法。对于一个重视语法简洁、反感不必要复杂性的语言来说,这似乎是一种异常情况。

有人能解释一下它为什么会这样吗?Common Lisp风格的defclass是否已经足够了呢?

2个回答

98
这是三个不同因素的混合体:
  1. jvm特定类型系统
  2. 在定义类型时,针对不同用例需要略微不同的语义
  3. 一些早期开发的内容和随着语言演进逐渐产生的新特性。
首先,让我们考虑这些的作用。deftype 和 gen-class 都类似于为预编译定义命名类。gen-class先出现,然后是clojure 1.2中的 deftype。Deftype更受欢迎,具有更好的性能特征,但更加限制。deftype类可以符合接口,但不能继承另一个类。
reify 和 proxy 都用于在运行时动态创建匿名类的实例。Proxy先出现,reify与clojure 1.2中的deftype和defrecord同时出现。当语义不太严格时,更喜欢使用 reify,就像喜欢使用 deftype 一样。
那么为什么会同时有deftype和defrecord呢?对于大多数情况,我们将要使用defrecord:它具有各种我们所知道和喜欢的clojure优点,如可序列化等。deftype则用于作为其他数据结构实现的底层构建块。它不包括常规 clojure 接口,但可以选择可变字段(尽管这不是默认值)。
了解更多信息请查看:

clojure.org 数据类型页面

deftype 和 reify 被引入的 google group 线程


1
这个 Google 群组的讨论非常有价值。我的理解是,在 Java 互操作代理方面,gen-class 大多被 reify 和 deftype 取代了。我很高兴我们现在有更少的“推荐”定义类型的方式。 - Salil
2
@Trylks:将对象视为键值对序列的能力。几乎所有原生于Clojure的东西都可以被视为序列,这非常强大。 - Rob Lachlan
“代理(proxy)”有时是首选,因为它允许在运行时修改方法。(例如,《Clojure之乐》第2版建议,在Web处理程序中修改回调可能很方便。) - Mars

59
简短回答是它们都有不同且实用的目的。 复杂性是由于需要与底层JVM的不同功能有效地互操作。如果您不需要任何Java interop,则99%的情况下最好坚持使用defrecord或简单的Clojure map。如果您想要使用协议,请使用defrecord。否则,普通的Clojure map可能是最简单和最易懂的。如果您的需求更加复杂,则以下流程图是说明为什么选择其中一种选项而不是其他选项的绝佳工具:

http://cemerick.com/2011/07/05/flowchart-for-choosing-the-right-clojure-type-definition-form/

Flowchart for choosing the right clojure type definition form


2
非常感谢提供流程图的链接。博客作者是O'Reilly的《Programming Clojure》合著者,这很有帮助。我认为决策制定相当复杂。接口和具体类之间的区别,是否需要定义静态方法或命名类型或匿名类型等差异如此之大,以至于需要不同的语言结构吗? - Salil
4
区别并不是很大,但在你所追求的目标上有哲学上的差异。我认为Clojure中的多个方法反映了这些根本性的差异,这也是它们应该被赋予不同名称的一个好理由。 - mikera
流程图很好,但并不涵盖所有可能性或使用一个或另一个选项或它们的组合的原因。没有简单的图表可以做到这一点。 - Mars

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接