假设我有一个这样定义一个Erlang的演员:
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。 但是,二郎演员似乎做对象应该怎么做:保持状态,封装,并提供了基于消息的接口。
换句话说,当我路过二郎山演员之间的消息,是怎么回事,当我路过Ruby对象之间的消息比有什么不同?
我怀疑有对功能/ OOP二分法比我看到的更大的后果。 任何人都可以指出来?
让我们抛开事实二郎演员将由VM来调度,从而可以与其它代码同时运行。 我知道这是Erlang和Ruby版本之间的主要区别,但是这不是我在获得。 并发有可能在其他语言,包括Ruby。 虽然Erlang的并发执行可能非常不同(有时更好),我真的不问的性能差异。
相反,我更感兴趣的问题的功能,VS-OOP侧。
换句话说,当我路过二郎山演员之间的消息,是怎么回事,当我路过Ruby对象之间的消息比有什么不同?
所不同的是,在像Ruby传统语言没有消息传递但那是在同一个线程执行的,如果你有多线程应用程序,这可能会导致同步问题的方法调用。 所有线程都可以访问对方线程的内存。
在二郎所有演员都是独立的,改变另一个演员的状态,唯一的办法就是要发送消息。 没有过程可以访问任何其他进程的内部状态。
恕我直言,这不是FP VS OOP最好的例子。 差异通常在访问/迭代和对象上的链接方法/函数的清单。 此外,也许理解什么是“当前状态”工作在FP更好。
在这里,你把对对方两个非常不同的技术。 一个碰巧是F,另一个OO。
我可以当场马上第一个区别是内存隔离。 消息连载中二郎,所以它更容易避免竞争条件。
第二类是内存管理的细节。 Erlang中的消息处理是发送者和接收者之间的下方划分。 有两组由二郎山VM保存的进程结构的锁。 因此,尽管发送方发送他获取锁,其不阻止主处理操作的信息(由主锁访问)。 归纳起来,它给二郎更加柔和的实时性VS在Ruby端完全随机的行为。
从外面看,演员像对象。 它们封装状态,并通过消息与世界其他地区的沟通来操纵该状态。
要查看FP是如何工作的,你必须寻找一个演员里面,看看它是如何变异的状态。 您的例子状态是一个整数过于简单。 我没有时间去提供完整的例子,但我会勾画的代码。 通常情况下,一个演员循环看起来如下:
loop(State) ->
Message = receive
...
end,
NewState = f(State, Message),
loop(NewState).
从OOP最重要的区别在于有没有变量突变,即NewState从国家获得的,并用它可以共享大部分数据,但状态变量始终保持不变。
这是一个很好的性质,因为我们从来没有腐败的当前状态。 函数f通常会进行一系列改造,把国家变成NewState的。 只有当/当它完全成功,我们通过调用循环(NewState)取代旧的国家用新的。 因此,重要的好处是我们国家的一致性。
我发现的第二个好处是干净的代码,但它需要时间来适应它一些时间。 一般来说,因为你不能修改变量,你将有很多非常小的功能划分代码。 这实际上是很好的,因为你的代码将得到很好的因素。
最后,因为你不能改变一个变量,它更容易推理的代码。 随着可变对象,你不能确定你的对象的某一部分是否会被修改,而且如果使用全局变量得到逐步恶化。 做FP时,您应该不会遇到这样的问题。
尝试一下,你应该尝试使用纯二郎结构(不是演员,ETS,Mnesia的或PROC字典)来操作的功能方式的一些更复杂的数据。 或者,你可能会尝试在红宝石这
二郎包括艾伦凯的OOP(Smalltalk中),并从Lisp的功能编程的消息传递的方法。
你在你的例子说明什么是OOP消息的方法。 在Erlang进程发送消息类似于艾伦凯的对象发送消息的概念。 顺便说一句,你可以检索这个概念也implemtented在划痕其中并联运行物体之间发送消息。
功能编程是你如何编写代码的过程。 例如,在二郎变量不能被修改。 一旦它们被设置,你只能阅读。 你也有一个列表数据结构, 工作原理很像Lisp的列表,你有乐趣这是由Lisp的拉姆达insprired。
消息传递在一侧,而在另一侧上的功能是在二郎相当两个单独的东西。 当编码现实生活中的二郎神的应用程序,你花你98%的时间做函数式编程和2%,考虑其主要用于可扩展性和并发性传递,消息。 要说它的另一种方式,当你来到tackly复杂的编程问题,你可能会使用二郎神的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的交易行为......恕我直言,这个概念是非常不同的一些原则,但你一定要试试,如果你要正确地使用它来思考不同。 我敢肯定,这是可以写在二郎山“OOPlike”的代码,但是这将是非常难以避免并发:O),并在年底没有优势。 看看OTP原则,给出了关于在二郎山应用程序架构的一些曲目(监督树,“1个单客户端服务器”,挂流程,监控流程的游泳池,当然模式匹配单分配,消息,节点集群... )。