请跟我重复:“这些是不同的范例”
大声地重复20次左右——这是我们目前的口头禅。
如果我们非要把苹果和橘子进行比较,那么至少要考虑它们作为“水果”的共性在哪里相交。
Java的“对象”是Java程序员计算的基本单位。也就是说,一个“对象”(基本上是一个有手有脚的结构体,具有比C++中更严格的封装
实施)是你建模世界的主要工具。你会想:“这个对象知道/有
Data {X,Y,Z}
,并执行
Functions {A(),B(),C()}
,随着它走到哪里就带着
Data
,并通过调用作为公共接口一部分定义的函数/方法与其他对象通信。它是一个名词,这个名词
做一些事情。” 也就是说,你的思维过程围绕这些计算单元展开。默认情况下,发生在对象之间的事情按顺序发生,崩溃会打断这个顺序。它们被称为“对象”,因此(如果我们忽略Alan Kay最初的意义),我们得到了“面向对象”。
Erlang的“进程”是Erlang程序员的基本计算单元。一个
进程(基本上是在自己的时间和空间中运行的自包含顺序程序)是Erlanger模拟世界的主要工具(1)。与Java对象定义封装级别类似,Erlang进程也定义了封装级别,但在Erlang的情况下,计算单位是
完全相互隔离的。您不能在另一个进程上调用方法或函数,也无法访问驻留在其中的任何数据,也没有一个进程在与其他进程相同的时间上下文中运行,也没有关于接收消息顺序的任何保证相对于可能正在发送消息的其他进程。它们可以像完全不同的星球一样(事实上,这实际上是可能的)。它们可以独立于彼此崩溃,只有在它们已经选择受到影响时才会影响其他进程(即使这也涉及消息传递:从死亡进程注册接收自杀通知,这本身并不保证按照整个系统的任何顺序到达,您可能会选择或不选择做出反应)。
Java处理复杂性直接采用复合算法:对象如何协同解决问题。它旨在在单个执行上下文中执行此操作,并且Java的默认情况是顺序执行。Java中的多个线程表示多个运行上下文,这是一个非常复杂的主题,因为不同时间上下文中的活动对彼此(以及整个系统)的影响很大,因此需要进行防御编程、异常方案等。在Java中说“多线程”意味着与Erlang中的说法不同,事实上,在Erlang中甚至从未这样说过,因为它始终是基本情况。请注意,Java线程意味着与时间相关的隔离,而不是内存或可见引用——在Java中,可见性通过手动选择私有和公共部分来控制;系统中普遍可访问的元素必须设计为“线程安全”和可重入,通过排队机制进行序列化,或者采用锁定机制。简而言之:调度是线程/并发Java程序中手动管理的问题。
Erlang将每个进程的运行上下文按照执行时间(调度)、内存访问和引用可见性进行分离,从而通过完全隔离简化算法的每个组成部分。这不仅是默认情况,也是该计算模型下唯一可用的情况。这样做的代价是,在处理序列穿过消息障碍后,永远无法确切地知道任何给定操作的顺序——因为消息本质上都是网络协议,没有方法调用可以保证在给定上下文中执行。这相当于为每个对象创建一个JVM实例,并只允许它们通过套接字进行通信——在Java中这将非常麻烦,但这是Erlang设计工作方式的基础(顺便说一句,如果去除与Web相关的包袱,编写“Java微服务”的概念也是如此——Erlang程序默认情况下是大量微服务的集合)。这一切都是关于权衡的。
这些是不同的范式。我们能找到的最接近共性是从程序员的角度来看,Erlang进程类似于Java对象。如果我们必须找到与Java线程相似的东西......那么,在Erlang中我们根本找不到这样的可比概念,因为在Erlang中没有这样的概念。再强调一遍:
这些是不同的范式。如果您在Erlang中编写一些非平凡的程序,这将很快变得明显。
请注意,我说“这些是不同的范式”,但甚至没有触及面向对象编程(OOP)和函数式编程(FP)的主题。在“以Java思考”和“以Erlang思考”之间的区别比OOP与FP更为基本。(事实上,可以为Erlang VM编写一个像Java一样工作的OOP语言--例如:
Erlang中OOP对象的实现。)
尽管真实情况是,Erlang的"并发导向"或"进程导向"基础更接近Alan Kay在创造"面向对象"这个术语时所想表达的(2),但这并非重点。Kay的意图是通过将计算单元切割成离散块来降低系统的认知复杂度,并且隔离是必须的。Java通过一种特殊的语法结构围绕称为"class definitions"的高阶分派闭包来实现这一点,但仍然在本质上保持过程化。Erlang通过按对象拆分运行上下文来实现这一点。这意味着Erlang的东西不能相互调用方法,但Java的东西可以。这意味着Erlang的东西可能会孤立崩溃,但Java的东西则不会。这种基本差异带来了大量的影响——因此称之为"不同的范式"。权衡取舍。
注释:
- 顺便提一下,Erlang实现了一个版本的 "Actor模型",但我们不使用这个术语,因为Erlang的设计早于该模型的普及。Joe在设计Erlang和写他的论文时并不知道它。
- Alan Kay曾经详细阐述过他创造"面向对象"这个术语的含义,其中最有趣的是他对消息传递的看法(从具有其自身时序和内存的独立进程向另一个进程发出单向通知)与调用(在具有共享内存的顺序执行上下文中进行函数或方法调用)-以及编程语言呈现的编程接口和底层实现之间如何有些模糊。