clojure协议的简单解释

我想了解clojure协议,他们应该解决什么问题。 有没有人有clojure协议的什么和什么明确的解释?

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

那么,expression问题是什么? 它涉及到可扩展性的基本问题:我们的程序使用操作来操作数据types。 随着我们的计划的发展,我们需要用新的数据types和新的操作来扩展它们。 特别是,我们希望能够添加与现有数据types一起工作的新操作,并且希望添加与现有操作一起工作的新数据types。 我们希望这是真正的扩展 ,即我们不想修改现有的程序,我们要尊重现有的抽象,我们希望我们的扩展是独立的模块,在单独的名称空间,分别编译,分别部署,分别types检查。 我们希望他们是types安全的。 [注意:并非所有的语言都是有意义的。 但是,举例来说,让它们types安全的目标即使在像Clojure这样的语言中也是有意义的。 仅仅因为我们不能静态地检查types安全性并不意味着我们希望我们的代码随机破坏,对吗?

expression式问题是,你如何在一种语言中实际提供这样的可扩展性?

事实certificate,对于程序和/或函数式编程的典型朴素实现,添加新操作(过程,函数)是非常容易的,但是很难添加新的数据types,因为基本上操作使用一些数据types( switchcase ,模式匹配),您需要添加新的案例,即修改现有的代码:

 func print(node): case node of: AddOperator => print(node.left) + '+' + print(node.right) NotOperator => '!' + print(node) func eval(node): case node of: AddOperator => eval(node.left) + eval(node.right) NotOperator => !eval(node) 

现在,如果你想添加一个新的操作,比如说types检查,这很容易,但是如果你想添加一个新的节点types,你必须在所有操作中修改所有现有的模式匹配expression式。

而对于典型的幼稚面向对象,则有完全相反的问题:添加与现有操作一起工作的新数据types(通过inheritance或覆盖它们)很容易,但很难添加新的操作,因为这基本上意味着修改现有的类/对象。

 class AddOperator(left: Node, right: Node) < Node: meth print: left.print + '+' + right.print meth eval left.eval + right.eval class NotOperator(expr: Node) < Node: meth print: '!' + expr.print meth eval !expr.eval 

在这里,添加一个新的节点types很容易,因为你要么inheritance,重写或者实现所有需要的操作,但是添加一个新的操作是很困难的,因为你需要把它添加到所有的叶子类或者基类中,码。

几种语言有几个解决expression式问题的构造:Haskell有types类,Scala有隐含的参数,Racket有Units,Go有接口,CLOS和Clojure有Multimethods。 也有“解决scheme” 试图解决它,但以某种方式失败:C#和Java中的接口和扩展方法,Ruby中的MonkePatching,Python,ECMAScript。

请注意,Clojure实际上已经有了解决expression式问题的机制:Multimethods。 OO与EP的问题在于它们将操作和types捆绑在一起。 与Multimethods他们是分开的。 FP有的问题是他们把操作和案件歧视捆绑在一起。 再次,与Multimethods他们是分开的。

所以,我们来比较一下Protocols和Multimethods,因为两者都是一样的。 或者换一种说法:为什么协议如果已经有多方法

Protocols提供的Multimethods主要是Grouping:可以将多个function组合在一起,并将这三个function组合在一起形成Protocol Foo 。 你不能用Multimethods做到这一点,他们总是站在自己的立场上。 例如,你可以声明Stack协议同时包含一个push和一个pop函数。

那么,为什么不添加将Multimethods分组在一起的function呢? 有一个纯粹的实用的原因,这就是为什么我在我的介绍性语句“performance”中使用了“efficient”这个词。

Clojure是一种托pipe语言。 即它是专门devise在另一种语言的平台之上运行的。 事实certificate,您希望Clojure运行的任何平台(JVM,CLI,ECMAScript,Objective-C)都具有专门的高性能支持, 用于第一个参数types的调度。 Clojure Multimethods OTOH派发所有参数的 任意属性

所以,协议限制你在第一个参数上发送,而在它的types上(或作为nil上的特殊情况)发送。

这不是对协议本身的限制,它是访问底层平台性能优化的实用select。 特别是,这意味着协议对JVM / CLI接口有一个简单的映射,这使得它们非常快速。 事实上,事实上,我们可以用Clojure本身来重写当前用Java或C#编写的Clojure的那些部分。

Clojure实际上已经从版本1.0开始具有协议:例如, Seq是一个协议。 但直到1.2,你不能在Clojure中编写协议,你必须用主机语言编写它们。

我认为将协议想象为在面向对象的语言(如Java)中的“接口”在概念上是相当有帮助的。 一个协议定义了一个抽象的函数集,可以以一个具体的方式实现给定的对象。

一个例子:

 (defprotocol my-protocol (foo [x])) 

用一个名为“foo”的函数定义一个协议,该函数作用于一个参数“x”。

然后可以创build实现该协议的数据结构,例如

 (defrecord constant-foo [value] my-protocol (foo [x] value)) (def a (constant-foo. 7)) (foo a) => 7 

请注意,这里实现该协议的对象作为第一个参数x传递 – 就像面向对象语言中的隐式“this”参数一样。

协议非常强大和有用的function之一是, 即使对象最初不是为支持协议而devise的 ,也可以将它们扩展为对象。 例如,如果你喜欢,你可以将上面的协议扩展到java.lang.String类:

 (extend-protocol my-protocol java.lang.String (foo [x] (.length x))) (foo "Hello") => 5