clojure协议的简单说明 [英] Simple explanation of clojure protocols

查看:140
本文介绍了clojure协议的简单说明的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图理解clojure协议和他们应该解决什么问题。有没有人清楚解释clojure协议的什么和什么?

I'm trying to understand clojure protocols and what problem they are supposed to solve. Does anyone have a clear explanation of the whats and whys of clojure protocols?

推荐答案

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

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

那么,什么是表达式问题?它指的是可扩展性的基本问题:我们的程序使用操作来处理数据类型。随着我们的程序发展,我们需要使用新的数据类型和新的操作来扩展它们。特别是,我们希望能够添加与现有数据类型一起使用的新操作,并且我们希望添加与现有操作配合使用的新数据类型。 我们希望这是真实的扩展,即我们不想修改现有程序,我们要尊重现有的抽象,我们希望我们的扩展是单独的模块,在单独的命名空间,单独编译,单独部署,单独类型检查。我们希望它们是类型安全的。 [注意:并非所有这些在所有语言中都有意义。但是,例如,使它们类型安全的目标即使在像Clojure这样的语言中也是有意义的。只是因为我们无法静态地检查类型安全并不意味着我们希望我们的代码随机中断。]

So, what's the Expression Problem? It refers to the basic problem of extensibility: our programs manipulate data types using operations. As our programs evolve, we need to extend them with new data types and new operations. And particularly, we want to be able to add new operations which work with the existing data types, and we want to add new data types which work with the existing operations. And we want this to be true extension, i.e. we don't want to modify the existing program, we want to respect the existing abstractions, we want our extensions to be separate modules, in separate namespaces, separately compiled, separately deployed, separately type checked. We want them to be type-safe. [Note: not all of these make sense in all languages. But, for example, the goal to have them type-safe makes sense even in a language like Clojure. Just because we can't statically check type-safety doesn't mean that we want our code to randomly break, right?]

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

The Expression Problem is, how do you actually provide such extensibility in a language?

事实证明,对于典型的程序性和/添加新的操作(过程,函数),但是很难添加新的数据类型,因为基本上操作使用某种情况区分( switch case ,模式匹配),您需要向它们添加新的案例,即修改现有代码:

It turns out that for typical naive implementations of procedural and/or functional programming, it is very easy to add new operations (procedures, functions), but very hard to add new data types, since basically the operations work with the data types using some sort of case discrimination (switch, case, pattern matching) and you need to add new cases to them, i.e. modify existing code:

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)



现在,如果你想添加一个新的操作, ,这很容易,但如果你想添加一个新的节点类型,你必须修改所有操作中所有现有的模式匹配表达式。

Now, if you want to add a new operation, say, type-checking, that's easy, but if you want to add a new node type, you have to modify all the existing pattern matching expressions in all operations.

对于典型的naive OO,你有完全相反的问题:很容易添加新的数据类型,使用现有的操作(通过继承或覆盖它们),但是很难添加新的操作,因为这基本上意味着修改现有的类/对象。 / p>

And for typical naive OO, you have the exact opposite problem: it is easy to add new data types which work with the existing operations (either by inheriting or overriding them), but it is hard to add new operations, since that basically means modifying existing classes/objects.

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

在这里,添加一个新的节点类型很容易,因为你继承,覆盖或实现所有需要的操作,但是添加一个新的操作是很困难的,因为你需要将它添加到所有叶类或基类,从而修改现有代码。

Here, adding a new node type is easy, because you either inherit, override or implement all required operations, but adding a new operation is hard, because you need to add it either to all leaf classes or to a base class, thus modifying existing code.

几种语言有几个解决表达式的构造问题:Haskell有类型类,Scala有隐含参数,Racket有Units,Go有Interfaces,CLOS和Clojure有Multimethods。还有解决方案,尝试来解决它,但失败以某种方式:C#和Java中的接口和扩展方法,Ruby,Python,ECMAScript中的Monkeypatching。

Several languages have several constructs for solving the Expression Problem: Haskell has typeclasses, Scala has implicit arguments, Racket has Units, Go has Interfaces, CLOS and Clojure have Multimethods. There are also "solutions" which attempt to solve it, but fail in one way or another: Interfaces and Extension Methods in C# and Java, Monkeypatching in Ruby, Python, ECMAScript.

请注意,Clojure实际上已经具有用于解决表达式问题的机制:多方法。 OO与EP的问题是它们将操作和类型捆绑在一起。使用Multimethods,它们是分开的。 FP具有的问题是它们将操作和情况区分捆绑在一起。再次,使用Multimethods他们是分开的。

Note that Clojure actually already has a mechanism for solving the Expression Problem: Multimethods. The problem that OO has with the EP is that they bundle operations and types together. With Multimethods they are separate. The problem that FP has is that they bundle the operation and the case discrimination together. Again, with Multimethods they are separate.

因此,让我们比较协议与多方法,因为两者做同样的事情。或者,换句话说:为什么协议如果我们已经有

So, let's compare Protocols with Multimethods, since both do the same thing. Or, to put it another way: Why Protocols if we already have Multimethods?

协议提供多个方法的主要是分组:将多个函数组合在一起并将这三个函数一起形成协议 Foo 。你不能用Multimethods做,他们总是站在自己。例如,您可以声明 Stack 协议由 a push pop

The main thing Protocols offer over Multimethods is Grouping: you can group multiple functions together and say "these 3 functions together form Protocol Foo". You cannot do that with Multimethods, they always stand on their own. For example, you could declare that a Stack Protocol consists of both a push and a pop function together.

所以,为什么不添加组功能Multimethods一起?有一个纯粹务实的原因,这就是为什么我在我的介绍性句子中使用高效一词:性能。

So, why not just add the capability to group Multimethods together? There's a purely pragmatic reason, and it is why I used the word "efficient" in my introductory sentence: performance.

Clojure是一种托管语言。也就是说它专门设计为在另一个语言平台之上运行。事实证明,几乎任何你希望Clojure运行的平台(JVM,CLI,ECMAScript,Objective-C)都具有专门的高性能支持,可以单独调度 论据。 Clojure Multimethods OTOH在所有参数的任意属性上分派

Clojure is a hosted language. I.e. it is specifically designed to be run on top of another language's platform. And it turns out that pretty much any platform that you would like Clojure to run on (JVM, CLI, ECMAScript, Objective-C) has specialized high-performance support for dispatching solely on the type of the first argument. Clojure Multimethods OTOH dispatch on arbitrary properties of all arguments.

(或作为 nil 的特殊情况)仅在第一参数和

So, Protocols restrict you to dispatch only on the first argument and only on its type (or as a special case on nil).

这不是对协议本身的想法的限制,它是一个务实的选择,以获得底层平台的性能优化。特别是,它意味着协议有一个平凡的映射到JVM / CLI接口,这使得它们非常快。足够快,事实上,能够重写Clojure的那些部分,目前用Java或C#编写的Clojure本身。

This is not a limitation on the idea of Protocols per se, it is a pragmatic choice to get access to the performance optimizations of the underlying platform. In particular, it means that Protocols have a trivial mapping to JVM/CLI Interfaces, which makes them very fast. Fast enough, in fact, to be able to rewrite those parts of Clojure which are currently written in Java or C# in Clojure itself.

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

Clojure has actually already had Protocols since version 1.0: Seq is a Protocol, for example. But until 1.2, you couldn't write Protocols in Clojure, you had to write them in the host language.

这篇关于clojure协议的简单说明的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆