Clojure协议与Scala结构类型

时间:2010-12-22 10:16:48

标签: scala clojure protocols language-design structural-typing

在Clojure 1.2中the interview with Rich Hickey上观看Protocols,并且对Clojure知之甚少之后,我对Clojure协议提出了一些问题:

  • 他们是否打算在Scala中执行与Structural Types相同的操作?协议对结构类型(性能,灵活性,代码清晰度等)有什么好处?它们是通过反思实现的吗?
  • 与Scala的互操作性问题:可以使用协议代替Scala中的结构类型吗?它们可以在Scala中扩展(如果'扩展'术语可以应用于协议吗?

4 个答案:

答案 0 :(得分:15)

完全无关。

Scala是一种静态类型语言。 Clojure是一种动态类型语言。这种差异从根本上影响了它们。

结构类型是静态类型,句点。它们只是让编译器静态证明一个对象具有特定结构的一种方式(我说这里证明,但是铸造可能会一如既往地造成虚假证据)。

Clojure中的协议是一种创建动态调度的方法,它比反射或在地图中查找更快。从语义上讲,它们并没有真正扩展Clojure的功能,但在操作上它们比以前使用的机制要快得多。

与Java接口一样,Scala特性更接近协议,但同样存在静态与动态问题。 Scala特征必须在编译时与类关联,类似于Java接口。在事实之后,甚至可以由第三方将Clojure协议添加到数据类型中。

像Java和Scala这样的Clojure协议可以通过包装器/代理模式或动态代理(http://download.oracle.com/javase/1.4.2/docs/guide/reflection/proxy.html)等机制实现。但是这些将比Clojure协议更加笨拙,并且使对象身份正确也很棘手。

答案 1 :(得分:10)

Clojure中协议的目的是以有效的方式解决表达式问题。

[见:Simple explanation of clojure protocols.]

Scala对表达式问题的解决方案是隐含的。因此,在语义上, 是最接近Scala中Clojure协议的等价物。 (在Haskell中,它将是Typeclasses或类型系列。)

答案 2 :(得分:6)

正如我从introductory blogpost所理解的那样,封闭协议更接近于Scala Traits,而不是结构类型(因此,不能用它来替代它们,回答我的第二个问题):

/* ----------------------- */
/* --- Protocol definition */
/* ----------------------- */

(defprotocol Fly
  "A simple protocol for flying"
  (fly [this] "Method to fly"))

/* --- In Scala */    
trait Fly{
    def fly: String
}

/* --------------------------- */
/* --- Protocol implementation */
/* --------------------------- */

(defrecord Bird [nom species]
  Fly
  (fly [this] (str (:nom this) " flies..."))

/* --- In Scala */    
case class Bird(nom: String, species: String) extends Fly{
    def fly = "%s flies..." format(nom)
}

/* --------------------- */
/* --- Dynamic extension */
/* --------------------- */

(defprotocol Walk
  "A simple protocol to make birds walk"
  (walk [this] "Birds want to walk too!"))

(extend-type Bird
  Walk
  (walk [this] (str (:nom this) " walks too..."))

/* --- In Scala */    
trait Walk{
    def walk = "Birds want to walk too!"
}

implicit def WalkingBird(bird: Bird) = new Walk{
    override def walk = "%s walks too..." format(bird.nom)
}

/* --------------- */
/* --- Reification */
/* --------------- */

(def pig (reify
                Fly (fly [_] "Swine flu...")
                Walk (walk [_] "Pig-man walking...")))

/* --- In Scala */    
object pig extends Fly with Walk{
    def fly = "Swine flu..."
    override def walk = "Pig-man walking..."
}

答案 3 :(得分:4)

其他答案更好地说明了问题的其他部分,但是:

  

是否通过实施   反射?

否 - 协议被编译为JVM接口。实现协议(reify,defrecord等)的东西被编译为实现协议接口的JVM类,因此对协议函数的调用与标准JVM方法调用相同。

这实际上是协议的激励因素之一 - 由于速度原因,很多Clojure的内部数据结构都是用Java编写的,因为在纯Clojure中无法进行全速多态分派。协议提供了这一点。 Clojure的源代码中仍然有很多Java,但现在可以在Clojure中重写所有内容而不会损失性能。