为什么Clojure有5种方法来定义一个类而不是仅一种?


84

Clojure具有gen-class,reify,proxy以及deftype和defrecord来定义类新的数据类型。对于一种重视语法简单性并且讨厌不必要的复杂性的语言,这似乎是一种畸变。有人可以解释为什么会这样吗?普通的Lisp风格的defclass是否足够?

Answers:


90

这是三个不同因素的混合:

  1. jvm的特定类型系统
  2. 在定义类型时,对于不同用例需要略微不同的语义
  3. 随着语言的发展,其中一些是较早开发的,有些则是后来开发的。

首先,让我们考虑一下这些功能。 deftypegen-class相似之处在于,它们都为提前编译定义了一个命名类。Gen-class首先出现,其次是Clojure 1.2中的deftype。Deftype是首选,并且具有更好的性能特征,但限制性更强。deftype类可以符合接口,但不能从另一个类继承。

Reifyproxy都用于在运行时动态创建匿名类的实例。Proxy首先出现,Clojure 1.2中的deftype和defrecord伴随着reify。与deftype一样,在语义不太严格的情况下,最好使用Reify。

剩下的问题是,为什么deftype和defrecord会同时出现并起相似的作用。对于大多数目的,我们将要使用defrecord:它具有我们知道和喜欢的所有Clojure优点,可序列性等等。Deftype旨在用作实现其他数据结构的低级构建块。它不包括常规的clojure接口,但是它具有可变字段的选项(尽管这不是默认值)。

如需进一步阅读,请查看:

clojure.org数据类型页面

引入了deftype和reify的Google组线程


1
谷歌组线程是非常有价值的。我的理解是对于java-interop代理,gen-class大部分被reify和deftype取代。我很高兴现在我们有更少的“推荐”方式来定义类型。
Salil

2
@Trylks:将对象视为键值对序列的能力。Clojure固有的几乎所有内容都可以视为一个序列,它非常强大。
罗布·拉克兰

proxy有时是首选,因为它允许在运行时修改方法。(例如,《 Clojure的欢乐》,第二版,建议您修改Web处理程序中的回调可能很方便。)
火星

52

简短的答案是,它们都有不同的用途。复杂性是由于需要与底层JVM的不同功能进行有效互操作。

如果您不需要任何Java互操作,那么最好在99%的时间坚持使用defrecord或简单的Clojure映射。

  • 如果要使用协议,请使用defrecord
  • 否则,常规的Clojure映射可能是最简单且最容易理解的

如果您的需求更加复杂,那么以下流程图是一个很好的工具,用于解释为什么您会选择其中一个而不是其他选项:

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

选择正确的Clojure类型定义表格的流程图


1
非常感谢您与流程图的链接。该博客由O'reilly的-Programming Clojure的合著者提供帮助。我认为这一决策非常复杂。接口和具体类之间的区别是否很大,是否需要定义静态方法,命名类型或匿名类型是否如此之大,以至于它们需要不同的语言构造?
Salil

4
差异虽然不大,但从哲学上讲,您要达到的目标是不同的。我认为Clojure中的多种方法反映了这些潜在的差异,这是为什么要为其赋予不同名称的一个很好的理由。
mikera 2011年

流程图很好,但是没有涵盖使用一个或另一个选项或它们的组合的所有可能性或原因。没有简单的图表可以。
火星
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.