Clojure 协议与 Scala 结构类型

发布于 2024-10-08 21:39:51 字数 491 浏览 5 评论 0原文

观看对 Rich Hickey 的采访Clojure 1.2 中的协议,并且对 Clojure 知之甚少,我对 Clojure 协议有一些疑问:

  • 它们是否打算做与 Scala 中的结构类型?与结构类型相比,协议有哪些优势(性能、灵活性、代码清晰度等)?它们是通过反思来实现的吗?
  • 关于与 Scala 的互操作性问题:Scala 中可以使用协议代替结构类型吗?它们可以在 Scala 中扩展(如果“扩展”术语可以应用于协议)吗?

After watching the interview with Rich Hickey on Protocols in Clojure 1.2, and knowing very little about Clojure, I have some questions on Clojure Protocols:

  • Are they intended to do the same thing as Structural Types in Scala? What benefits do Protocols have over Structural Types (performance, flexibility, code clarity, etc.)? Are they implemented through reflections?
  • Questions on interoperability with Scala: Can Protocols be used instead of Structural Types in Scala? Can they be extended (if 'extension' term can be applied to Protocols) in Scala?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

樱花细雨 2024-10-15 21:39:51

完全不相关。

Scala 是一种静态类型语言。 Clojure 是一种动态类型语言。这种差异从根本上塑造了两者。

结构类型是静态类型,句号。它们只是让编译器静态证明对象将具有特定结构的一种方法(我在这里说证明,但强制转换一如既往会导致虚假证明)。

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

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

像 Clojure 协议这样的东西在 Java 和 Scala 中可以通过包装器/代理模式或动态代理( http://download.oracle.com/javase/1.4.2/docs/guide/reflection/proxy.html )。但这些会比 Clojure 协议笨拙得多,而且正确获取对象标识也很棘手。

Totally unrelated.

Scala is a statically typed language. Clojure is a dynamically typed language. This difference shapes both of them fundamentally.

Structural types are static types, period. They're just a way to have the compiler prove statically that an object will have a particular structure (I say prove here, but casting can cause bogus proofs as always).

Protocols in Clojure are a way to create dynamic dispatch that is much faster than reflection or looking things up in a map. In a semantic sense they don't really extend the capabilities of Clojure, but operationally they are significantly faster than the mechanisms used before.

Scala traits are a bit closer to protocols, as are Java interfaces, but again there's a static vs dynamic issue. Scala traits must be associated with a class at compile time, similar to Java interfaces. Clojure protocols can be added to a datatype at runtime after the fact even by a third party.

Something like Clojure protocols is possible in Java and Scala through mechanisms like wrapper/proxy patterns or dynamic proxies ( http://download.oracle.com/javase/1.4.2/docs/guide/reflection/proxy.html ). But those will be a heck of a lot clumsier than Clojure protocols and getting object identity right is tricky as well.

黯然#的苍凉 2024-10-15 21:39:51

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

[请参阅:clojure 协议的简单说明]

Scala 对表达式问题的解决方案是隐式的。因此,从语义上来说,that 与 Scala 中的 Clojure 协议最接近。 (在 Haskell 中,它是类型类或者类型族。)

The purpose of Protocols in Clojure is to solve the Expression Problem in an efficient manner.

[See: Simple explanation of clojure protocols.]

Scala's solution to the Expression Problem are Implicits. So, semantically, that is the closest equivalent to Clojure Protocols in Scala. (In Haskell, it would be Typeclasses or maybe Type Families.)

谎言 2024-10-15 21:39:51

正如我从这篇介绍性博文中了解到的,闭包协议更接近 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..."
}

As I understood from this introductory blogpost, Closure Protocols are closer to Scala Traits, rather than Structural Types (and thus, cannot be used as a replacement for them, answering my second question):

/* ----------------------- */
/* --- 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..."
}
淤浪 2024-10-15 21:39:51

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

它们是通过以下方式实现的吗?
反思?

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

这实际上是协议的动机之一——出于速度原因,Clojure 的许多内部数据结构都是用 Java 编写的,因为没有办法在纯 Clojure 中进行全速多态调度。协议提供了这一点。 Clojure 的源代码中仍然包含大量 Java,但现在所有这些都可以在 Clojure 中重写,而不会损失性能。

Other answers speak to the other parts of your question better, but:

Are they implemented through
reflections?

No - protocols are compiled to JVM interfaces. Things which implement protocols (reify, defrecord, etc) are compiled to JVM classes which implement the protocol interface, so calls to protocol functions are the same as standard JVM method calls, under the hood.

That was actually one of the motivators for protocols - a lot of Clojure's internal data structures were written in Java, for speed reasons, because there was no way to do full-speed polymorphic dispatch in pure Clojure. Protocols provide that. Clojure still has a lot of Java in its source code, but that could all can now be rewritten in Clojure with no loss of performance.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文