Erlang actor与OOP对象有何不同?

假设我有一个像这样定义的Erlang actor:

counter(Num) -> receive {From, increment} -> From ! {self(), new_value, Num + 1} counter(Num + 1); end. 

同样,我有一个像这样定义的Ruby类:

 class Counter def initialize(num) @num = num end def increment @num += 1 end end 

Erlang代码以函数式编写,使用尾递归来维护状态。 但是,这种差异的有意义的影响是什么? 对于我天真的眼睛,这两个东西的接口看起来大致相同:你发送一条消息,状态得到更新,你得到了新状态的表示。

函数式编程经常被描述为与OOP完全不同的范例。 但是Erlang actor似乎完全按照对象应该做的:维护状态,封装并提供基于消息的接口。

换句话说,当我在Erlang actor之间传递消息时,它与我在Ruby对象之间传递消息的方式有何不同?

我怀疑function/ OOP二分法的影响比我看到的要大。 有谁可以指出他们?

让我们撇开这样一个事实,即Erlang actor将由VM调度,因此可以与其他代码同时运行。 我意识到这是Erlang和Ruby版本之间的一个主要区别,但这不是我所得到的。 在其他语言中可以实现并发,包括Ruby。 虽然Erlang的并发性可能表现得非常不同(有时甚至更好),但我并不是真的在询问性能差异。

相反,我对问题的function与OOP方面更感兴趣。

换句话说,当我在Erlang actor之间传递消息时,它与我在Ruby对象之间传递消息的方式有何不同?

区别在于像Ruby这样的传统语言没有消息传递,但是在同一个线程中执行的方法调用如果你有multithreading应用程序,这可能会导致同步问题。 所有线程都可以访问彼此的线程内存。

在Erlang中,所有actor都是独立的,改变另一个actor状态的唯一方法就是发送消息。 没有进程可以访问任何其他进程的内部状态。

恕我直言,这不是FP与OOP的最佳例子。 差异通常表现在访问/迭代和链接对象上的方法/函数。 此外,可能,理解什么是“当前状态”在FP中更好地工作。

在这里,您将两种截然不同的技术相互对立。 一个恰好是F,另一个是OO。

我可以立即发现的第一个区别是内存隔离。 消息在Erlang中序列化,因此更容易避免竞争条件。

第二个是内存管理细节。 在Erlang中,消息处理在Sender和Receiver之间划分。 Erlang VM拥有两组进程结构锁。 因此,当Sender发送消息时,他获取的锁不会阻止主进程操作(由MAIN锁访问)。 总而言之,它使得Erlang在Ruby方面具有更加柔和的实时性和完全随机的行为。

从外面看,演员类似于物体。 它们封装状态并通过消息与世界其他地方进行通信来操纵该状态。

要了解FP的工作原理,您必须查看一个actor,看看它是如何改变状态的。 状态为整数的示例太简单了。 我没有时间提供完整的示例,但我将草拟代码。 通常,actor循环如下所示:

 loop(State) -> Message = receive ... end, NewState = f(State, Message), loop(NewState). 

与OOP最重要的区别在于没有变量突变,即NewState是从State获得的,并且可能与它共享大部分数据,但State变量始终保持不变。

这是一个不错的属性,因为我们永远不会破坏当前的状态。 函数f通常会执行一系列转换以将State转换为NewState。 并且只有当它完全成功时,我们通过调用loop(NewState)将旧状态替换为新状态。 因此,重要的好处是我们国家的一致性。

我找到的第二个好处是更干净的代码,但它需要一些时间来适应它。 通常,由于您无法修改变量,因此您必须在许多非常小的函数中划分代码。 这实际上很好,因为你的代码将被很好地考虑在内。

最后,由于您无法修改变量,因此更容易对代码进行推理。 对于可变对象,您永远无法确定对象的某些部分是否会被修改,如果使用全局变量,它会逐渐变得更糟。 在进行FP时不应该遇到这样的问题。

要尝试一下,你应该尝试使用纯erlang结构(不是actor,ets,mnesia或proc dict)以function方式操作一些更复杂的数据。 或者,您也可以尝试使用ruby

Erlang包括Alan Kay的OOP(Smalltalk)的消息传递方法和Lisp的函数编程。

您在示例中描述的是OOP的消息方法。 Erlang进程发送消息的概念类似于Alan Kay发送消息的对象。 顺便说一下,你也可以在Scratch中检索这个概念,其中并行运行的对象在它们之间发送消息。

函数式编程是您对进程进行编码的方式。 例如,无法修改Erlang中的变量。 一旦设置完毕,您只能阅读它们。 你还有一个列表数据结构,它的工作方式与Lisp列表很相似,你可以获得Lisp的lambda激发的乐趣 。

一边传递消息,另一边传递的function在Erlang中是两个独立的东西。 在对现实生活中的erlang应用程序进行编码时,您花费98%的时间进行函数式编程,2%的时间考虑消息传递,这主要用于可伸缩性和并发性。 换句话说,当你遇到复杂的编程问题时,你可能会使用Erlang的FP端来实现算法的细节,并使用消息传递来实现可伸缩性,可靠性等…

你觉得这怎么样:

 thing(0) -> exit(this_is_the_end); thing(Val) when is_integer(Val) -> NewVal = receive {From,F,Arg} -> NV = F(Val,Arg), From ! {self(), new_value, NV}, NV; _ -> Val div 2 after 10000 max(Val-1,0) end, thing(NewVal). 

当你产生进程时,它会自己生存,减少它的值直到它达到值0并将消息{‘EXIT’,this_is_the_end}发送到链接到它的任何进程,除非你负责执行如下的操作:

 ThingPid ! {self(),fun(X,_) -> X+1 end,[]}. % which will increment the counter 

要么

 ThingPid ! {self(),fun(X,X) -> 0; (X,_) -> X end,10}. % which will do nothing, unless the internal value = 10 and in this case will go directly to 0 and exit 

在这种情况下,您可以看到“对象”与应用程序的其余部分并行生存,它可以与外部交互,几乎没有任何代码,并且外部可以让他做你做过的事情。你知道什么时候编写和编译代码。

这是一个愚蠢的代码,但有一些原则用于实现应用程序,如mnesia事务,行为…恕我直言这个概念真的不同,但你必须尝试不同,如果你想正确使用它。 我很确定在Erlang中编写“OOPlike”代码是可能的,但是要避免并发是非常困难的:o),最后没有优势。 看看OTP原理,它给出了Erlang中应用程序架构的一些跟踪(监督树,“1个单个客户端服务器”池,链接进程,受监视进程,当然还有模式匹配单个赋值,消息,节点集…… )。