【问题标题】:How do Erlang actors differ from OOP objects?Erlang actor 与 OOP 对象有何不同?
【发布时间】:2013-02-15 09:00:20
【问题描述】:

假设我有一个像这样定义的 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 完全不同的范式。但 Erlang actor 似乎完全按照对象应该做的事情去做:维护状态、封装并提供基于消息的接口。

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

我怀疑功能/OOP 二分法的后果比我看到的要大。谁能指出来?

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

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

【问题讨论】:

  • IMO 该示例太小/孤立,无法显示有意义的差异。显然,在这种情况下,概念上的差异很小。在这个简单的例子中,其他考虑因素更为重要。

标签: ruby oop functional-programming erlang


【解决方案1】:

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

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

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

第二个是内存管理细节。在 Erlang 中,消息处理在 Sender 和 Receiver 之间划分。 Erlang VM 持有两组进程结构锁。因此,当 Sender 发送消息时,他获得了不会阻塞主进程操作的锁(通过 MAIN 锁访问)。总而言之,与 Ruby 端的完全随机行为相比,它为 Erlang 提供了更柔和的实时性。

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

    • 是的,这就是这里的核心区别。在 Ruby 和其他传统语言中,他们称之为消息传递,而在 Erlang 中消息传递。
    【解决方案3】:

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

    要了解 FP 的工作原理,您必须查看演员的内部并了解它如何改变状态。您的状态为整数的示例太简单了。我没有时间提供完整的示例,但我会草绘代码。通常,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)以函数式方式操作一些更复杂的数据。或者,您可以在 ruby​​ 中使用this

    进行尝试

    【讨论】:

      【解决方案4】:

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

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

      函数式编程是您对流程进行编码的方式。例如,Erlang 中的变量不能被修改。设置后,您只能读取它们。您还有一个列表数据结构,works 非常类似于 Lisp 列表,并且您有 fun,它受 Lisp 的 lambda 启发。

      一方面的消息传递和另一方面的函数在 Erlang 中是完全不同的两件事。在编写现实生活中的 erlang 应用程序时,您将 98% 的时间用于函数式编程,而将 2% 的时间用于考虑消息传递,这主要用于可伸缩性和并发性。换句话说,当你遇到复杂的编程问题时,你可能会使用 Erlang 的 FP 端来实现算法的细节,并使用消息传递来实现可扩展性、可靠性等......

      【讨论】:

        【解决方案5】:

        你怎么看:

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

        【讨论】:

          猜你喜欢
          • 2011-04-03
          • 1970-01-01
          • 2017-12-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-06-06
          • 2011-01-02
          • 2021-12-22
          相关资源
          最近更新 更多