2013-02-28 157 views
11

假设我已经定义了一个Erlang的演员是这样的:Erlang与OOP对象有什么不同?

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的角色似乎确实做了对象应该做的事情:维护状态,封装并提供基于消息的接口。

换句话说,当我在Erlang之间传递消息时,它与在Ruby对象之间传递消息时有什么不同?

我怀疑功能/ OOP二分法有比我看到的更大的结果。任何人都可以指出它们吗?

让我们抛开Erlang actor被VM安排的事实,因此可能会与其他代码同时运行。我意识到这是Erlang和Ruby版本之间的主要区别,但这不是我所掌握的。其他语言(包括Ruby)可能会发生并发。尽管Erlang的并发性可能表现得非常不同(有时更好),但我并没有真正问到性能差异。

相反,我对问题的功能与面向对象方面更感兴趣。

+0

IMO的例子太小/分离,以显示有意义的差异。很明显,在这种情况下,*概念*差异很小。在这个微不足道的例子中,其他考虑因素更重要。 – 2013-03-01 00:34:49

回答

9

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

区别在于,在传统语言(如Ruby)中,没有消息传递,但在同一线程中执行方法调用,如果您有多线程应用程序,这可能会导致同步问题。所有线程都可以访问其他线程内存。

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

+5

是的,这是这里的核心区别。在Ruby和其他传统语言中,他们**调用**消息传递,而在Erlang中则** **传递消息。 – rvirding 2013-03-01 21:37:31

+0

谢谢罗伯特! – 2013-03-01 23:47:07

0

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

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

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

第二个是内存管理细节。在Erlang中,消息处理在发送者和接收者之间被分开。 Erlang虚拟机拥有两组进程结构的锁。因此,当发送者发送消息时,他获取不阻止主进程操作的锁(由主锁访问)。总而言之,它让Erlang在Ruby方面更加柔和的实时性与完全随机的行为。

0

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

要了解FP的工作方式,您必须查看演员内部并了解其如何变化状态。状态是一个整数的例子太简单了。我没有时间提供完整的示例,但我会画出代码。通常情况下,一个演员循环看起来如下:

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

从OOP最重要的区别在于有没有变量突变,即NewState从国家获得的,并且可以共享大部分与它的数据,但状态变量总是保持不变。

这是一个不错的属性,因为我们从不破坏当前状态。函数f通常会执行一系列转换以将状态转换为NewState。只有当它完全成功时,我们才能通过调用循环(NewState)来替换旧状态。 所以重要的好处是我们国家的一致性。

我发现的第二个好处是代码更干净,但需要一些时间才能习惯它。通常,由于您无法修改变量,因此您必须将代码分成许多非常小的函数。这其实很好,因为你的代码将被很好地考虑。

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

要尝试一下,您应该尝试通过使用纯erlang结构(而不​​是演员,ets,mnesia或proc dict)以功能方式处理一些更复杂的数据。另外,你也可以用红宝石尝试它this

0

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

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

函数式编程是如何编写进程的代码。例如,Erlang中的变量不能被修改。一旦他们被设置,你只能阅读它们。你也有一个列表数据结构,它与Lisp列表非常相似,并且你有受到Lisp lambda启发的fun

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

0

你觉得这是什么:

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个单一客户机服务器”池,链接进程,受监控进程,当然还有模式匹配单个分配,消息,节点集群等)的跟踪。 )。