Simula

Simula，一种编译式的程式语言，由奥利-约翰·达尔与克利斯登·奈加特，在1960年代于挪威奥斯陆的挪威计算中心，开发出来了Simula I与Simula 67两代。它承继了ALGOL 60作为基础，被认为是第一个物件导向程式设计的程式语言。

Simula 67介入了对象、类、子类（后来惯称为子类继承超类）、虚过程，还有协程、离散事件模拟和特征性的垃圾收集。Simula的影响经常被低估，Smalltalk、CLU、C++、Object Pascal、Modula-3、Java和后来的很多编程语言，受到了Simula 67的启发。BETA是Simula的现代后继者。

## 历史

<image>

1957年，克利斯登·奈加特开始在挪威国防研究机构的Ferranti Mercury电脑上写模拟器，为此他需要更强大的程式语言。1962年1月，奥利-约翰·达尔开始跟他合作，他们受到ALGOL 60的启发。1962年5月，发展出第一份模拟器程式语言提议，语言取名为SIMULA，后改称为Simula I。此时，克利斯登·奈加特受到Sperry公司邀请，去协助他们开发UNIVAC 1100/2200系列电脑。UNIVAC软件部门的主管鲍伯·贝莫，力邀克利斯登·奈加特前往国际资讯处理联盟（IFIP）举行的第二次国际会议上，发表了论文“SIMULA－扩展ALGOL到离散事件网络的描述”。

1963年8月，挪威计算中心（NCC）购买到UNIVAC 1100/2200系列，在UNIVAC的合约同意下，奥利-约翰·达尔在这台电脑上安装以凯斯西储 1107 ALGOL 60的编译器来实作的Simula I。1965年1月，Simula I终于可以在UNIVAC 1107上完全的运作。Simula I在1968年被移植到了Burroughs大型系统 B5500电脑，以及后来苏联的Ural (计算机)电脑。

1965年，东尼·霍尔首次提出“记录类”（record class）构造的概念，1966年，克利斯登·奈加特与奥利-约翰·达尔二人，将Simula I的具有“准并行”性质的“进程”，扩展成了具有了记录类性质的广义进程，随即改称为“物件”。1967年5月，奈加特和达尔在奥斯陆举办的IFIP工作小组论坛中，发表了关于类与子类声明的论文，形成Simula 67的第一份定义文件。

1968年召开的会议，组成了SIMULA标准小组，并发表了第一份官方Simula标准文件“SIMULA 67通用基础语言”。在1960年代后期和1970年代早期，Simula 67主要实现于四个系统之上：挪威计算中心的UNIVAC UNIVAC 1100/2200系列，挪威计算中心的IBM System/360和IBM System/370，奥斯陆大学在Kjeller联合安装的CDC CDC 3000系列，和瑞典国防研究所（FOA）的DEC TOPS-10。

当前的Simula 67业界标准，是在1986年修订的“标准SIMULA”，它在1987年被接受为瑞典国家标准。它有四个主要实现：Simula研究实验室、Lund Simula、GNU Cim和Portable Simula Revisited。

Simula影响了Smalltalk，和后来的面向对象编程语言。C++语言和Java语言的创始人，都认可自己受到了Simula的重要影响。

## 语法和语义

Simula 67包含通用算法语言ALGOL 60的多数特征作为自己的子集，它是大小写不敏感的。Simula 67的中心概念是对象，对象是自给自足（self-contained）的一段程序即一个块实例，它拥有由一个类声明定义的自己的局部数据和行动（action）。类是一种过程，它有能力引起在它被调用后仍存活的一个块实例，而这些实例就叫做这个类的对象。为了操纵对象和相互关联对象，语言介入了链表处理设施。

Simula 67为了将整个程序执行组织为：对象所属的诸“行动阶段”的一个序列，而将其所必需的基本功能特征，包含在作为“系统类”的“环境类”之中。在环境类中有被称为“标准系统类”的模拟器类`Simulation`，它定义了充当系统时间轴的“定序集合”（sequencing set）对象`SQS`、进程类`Process`和事件通告类`EVENT_NOTICE`，定序集合的成员是事件通告对象，它通过其`PROC`特性提及一个进程对象；事件通告表示了对应这个进程对象的下一个行动阶段的一个事件，被调度在系统时间`EVTIME`时发生。

### 块

块是成序列的声明，跟随着成序列的语句，并被包围在关键字`begin`和`end`之间。块自身是一种语句，在Simula 67中，它包括子块和有前缀（prefixed）块，在语法上，子块就是无前缀的ALGOL 60的块，即匿名块和过程主体。

声明担负定义在程序中用到的量（quantity）的特定属性，并给它们关联上标识符，这包括：简单变量、数组、接转（`switch`）、过程、类和外来声明。块中的每个语句，都可以是块或复合语句。复合语句与块相比在语法上没有声明。

所有的块，自动地介入名称目录（nomenclature）的一个新的层级：在这个块内出现的标识符，可以通过合适的声明，而被指定为局部于所论及的这个块；这个标识符在这个块里面的所表示的实体，不存在于它的外面；这个标识符在这个块外面的所表示的任何实体，在这个块里面是不可见的；在Simula 67中，可通过连接或远程访问使它成为可见的。

除了表示标签的标识符之外，一个标识符，如果出现在一个块中，而且并非声明于这个块中，则它非局部变量于这个块。因为块中的语句自身可以是一个块，局部和非局部于一个块的概念，必须递归地去理解。

块是一种形式描述或模式，关乎汇聚的数据结构和关联的算法以及行动。当一个块执行的时候，生成这个块的一个动态实例。一个块实例，可以被认为是它的形式描述的文字复本。在计算机中，一个块实例可以采用某一种形式的内存区域，它包含需要的动态块信息，并包括空间来持有局部于这个块的变量的内容。

块的任何一个内部的块，仍然是一个模式，但是在其中出现的非局部变量标识符，标定了局部于在字面上外在包围（textually enclosing）的块实例的项目。非局部变量于内部的块的标识符绑定，对这个内部的块的任何随后的动态实例，保持有效。

块实例中的局部变量，标识了分配给块实例的内存片段。在进入一个块之中的时候，任何局部于这个块即在其中声明的变量，都会被初始化。局部于一个块的一个变量，是一个内存设备，其内容依据这个变量的类型，要么是一个值，要么是一个引用。

值类型可特征化为直接关联着一个可能值的集合，即这个类型的“值范围”。引用概念，对应于一个名字或一个“指针”的直观想法。在Simula 67中有两种引用类型，对象引用类型和文本引用。对于值类型不关联着引用概念。提供了引用值的机制，还反映了机器的寻址可能性；在特定简单情况下，一个引用可以被实现为一个存储的值的内存地址。

### 过程

过程与块相比，它有一个名字，可以在程序的一些不同地方提及，并且在调用时可以给出参数。过程和块都不可能建立到它或它内部的引用；对于一个给定块，有可能生成它的一些可以共存和交互的实例，例如递归过程的实例。

过程的参数传送模态，除了有传值调用和传名调用，在Simula 67中，又增加了传引用调用。过程的缺省的传送模态，在Simula 67中，对于值类型的参数是传值调用，对于所有其他类型的参数是传引用调用；故而在过程声明的参数规定中，增加了以`name`为前导的名字部分，用来指定所述及的参数采用传名调用。

在过程主体内传名调用的形式参数的每次出现，都引起对实际参数的一次求值。在Simula 67中，这个求值发生在过程语句的上下文中，就是说不会出现标识符冲突，因为过程主体和它的变量此时是不可见的。过程调用的执行，在有参数的情况下要经历如下步骤：建立形式参数块实例；求值对应于传值调用或传引用调用的实际参数，并将其结果赋值给形式参数块实例的对应变量；过程主体被初始化并开始执行。

真正（proper）过程，不定义特定类型的函数指定式的值，在Simula 67中，它被称为具有普遍（universal）类型，任何类型都从属（subordinate）于普遍类型。

### 类与对象

类声明定义一个程序（数据和行动）模式，而符合（conforming）这个模式的对象被称为“属于相同的类”。不同于Smalltalk 80等面向对象编程语言，Simula 67的类和后来的Modula-3的对象类型，不进行实例变量与类变量的区分。

对于一个给定对象，类声明中形式参数、在虚拟部分中规定的虚拟量，和声明为局部于类主体（body）的量，叫做这个对象的特性（attribute）。一个特性的声明或规定叫做特性定义。在1986年修订的语言标准中，通过保护规定可以限制类特性标识符的可见性。

类声明中的形式参数，不适用传名调用，这是为了保证实际参数在类所引起的对象存活期间不能消失。在规定部分中需要对每个形式参数进行规定，这些参数被当作局部于类主体的变量。类主体通常是一个块，即使它如语法形式所允许的那样是块以外的一个语句，也表现得如同一个块。一个分裂（split）主体表现为一个块，在其中符号`<span style="color:green;font-weight: bold;">inner</span>`表示一个虚设（dummy）语句。

Simula 67的类主体中除了有定义特性的声明，还可以有定义行动的语句。如果在一个类声明中有行动定义，则符合这个模式的行动，可以由属于这个类的所有对象执行。如果在类声明中没有指定行动，则定义了一类纯数据结构，很多后来的面向对象编程语言就是这样，将对象作为记录或结构的扩展。例如：

```ObjectPascal
class Order(number); integer number;
begin
    integer numberOfUnits, arrivalDate;
    real processingTime;
end;
```

属于特定类的对象，通过对象表达式中的对象生成式`<span style="color:green;font-weight: bold;">new</span>`来生成，并制作出到它的引用。例如，想要多少就能生成多少属于这个`Order`类的一个新对象：

```ObjectPascal
new Order(103);
```

属于一个对象的行动，可以都在就一个过程而言的一个序列中执行。这些行动，还可以作为一系列的独立子序列或“行动阶段”（active phase）来执行。在一个给定对象的两个行动阶段之间，可以出现任何数目的其他对象的行动阶段。

### 子类与类串接

一个类可以被用作到另一个类声明的“前缀”，从而将前缀所定义的性质，建造入这个新的类声明定义的对象之中。具有前缀类`C`和类标识符`D`的一个类声明，定义了类`C`的一个子类`D`。属于子类`D`的对象，由自身是类`C`的对象的“前缀部分”，和类`D`的类声明主体所描述的“主体部分”组成。这两部分串接而形成了一个复合对象，它可以用串接成的类声明来正式的描述，串接的过程被认为先于程序执行而发生。

类只能在定义它的块层级中用作前缀。前缀类自身也可以有前缀。类不能出现在自己的前缀序列中。子类被称为内部（inner）于前缀类，而前缀类被称为外部（outer）于子类，但不可将子类称为前缀类的内部类。这不同于后来的面向对象编程语言中的称谓：“派生类”扩展“基础类”，或“子类”继承“超类”。例如：

```ObjectPascal
Order class SingleOrder;
begin
    real setupTime, finishingTime, weight;
end;
SingleOrder class Plate;
begin
    real length, width;
end;
```

属于`Order`类的子类如`SingleOrder`类和`Plate`类的新对象，都含有`Order`类定义的数据，再加上在各种类声明中定义的补充数据。例如，属于`Plate`类的对象含有`Order`类、`SingleOrder`类和`Plate`类定义的所有数据。

一个标识符在一个块中的出现，如果并未处在远程标识符之中或在有重新定义的内部的块之中，则被称为“未托付”（uncommitted）出现。对于子类的声明及其前缀类串接成的类声明，在局部于子类的特性标识符，和局部于其前缀类的特性标识符，二者的未托付出现之间的可能冲突，将凭借对涉及到的局部于子类的特性标识符的适合的系统性变更来避免，但是对应虚拟量的标识符不变更。

对于前缀类和子类声明串接成的类声明，它的形式参数列表，由前缀类的形式参数列表，和附加的子类声明的参数列表二者合并而成。它的值部分、规定部分和虚拟部分，是前缀类和子类声明相应各部分的并集。结果的虚拟部分不能包含某个标识符的多于一次出现。

对于类主体，最初的`begin`和随后的声明被称为“块头部”，从第一个可执行语句直到`inner`之前的语句被称为“初始运算”，在`inner`之后的语句和最终的`end`被称为“复合尾部”。如果前缀类主体是分裂主体，用前缀类的块头部复本替代子类的块头部中的`begin`，将前缀类的初始运算复本插入到子类的块头部之后，用前缀类的复合尾部复本替代子类的复合尾部中的`end`。如果前缀类主体不是分裂主体，它被解释为如同将`;inner`插入到最终的`end`之前。

设`C<sub>n</sub>`是具有前缀序列`C<sub>1</sub>, C<sub>2</sub>, ……, C<sub>n-1</sub>`的一个类，这里类`C<sub>k</sub>` `(k = 1, 2, ……, n)`的下标`k`叫做前缀层级，`X`是属于类`C<sub>n</sub>`的一个对象，则`X`是一个复合对象；非正式的说，串接机制有如下结果：

- 对象`X`拥有的特性集合，是在序列`C<sub>1</sub>, C<sub>2</sub>, ……, C<sub>n</sub>`中诸类所定义特性集合的并集。在类`C<sub>k</sub>` `(1 <= k <= n)`中定义的特性，被称为定义在前缀层级`k`。
- 对象`X`拥有的“运算规则”，由来自这些类主体的诸语句构成，它们所属的前缀层级遵循规定的次序。来自类`C<sub>k</sub>`的语句，被称为属于对象`X`的前缀层级`k`。
- 在对象`X`的前缀层级`k`的语句，能访问在对象`X`的等于或外部于`k`的前缀层级上定义的它的所有特性，但是如果外部的类`C<sub>i</sub>` `(i < k)`中存在冲突定义，则导致对应特性不可见而不能直接访问。这些不可见特性仍有访问方式，例如通过使用过程或`this C<sub>i</sub>`。
- 在对象`X`的前缀层级`k`的语句，不能立即访问在对象`X`的内部于`k`的前缀层级上定义的它的那些特性，除非通过虚拟量。
- 在前缀层级`k`的分裂主体中，符号`inner`，表示对象`X`的属于内部于`k`的前缀层级的运算规则的诸语句，或者在`k = n`时表示虚设语句。如果序列`C<sub>1</sub>, ……, C<sub>n-1</sub>`都没有分裂主体，则在对象`X`的运算规则中诸语句所属的前缀层级遵循递增次序。

用作前缀的系统类，与其前缀链的所有的类一起，被当作声明于最小的包围其字面上出现的块中。因此重新声明可以出现在一个程序的内部的块层级中。

有前缀块的一个实例，是由块前缀生成的一个单一的对象，与这个主体块的实例，串接而成一个复合对象，其串接定义采用类似于类的串接规则。块前缀的类的形式参数，按块前缀的实际参数所示初始化。对有前缀块有两个限制：一个类如果在其中通过使用`this`来引用类自身，则它作为块前缀是非法的。块前缀的类标识符所提及的类，必须局部于最小的包围这个有前缀块的块。

### 对象引用

当属于各种类的很多对象，作为同一个程序的各个部分而共存的时候，需要能够对个体对象指定名字，为此介入了叫做“引用”的新的基本类型；还要能相互关联对象，比如通过二叉树和各种其他类型的列表结构。Simula 67将循环双向链表叫做“集合”，它将集合类`Simset`作为“标准系统类”介入为环境类的一部分。

对于某个类的一个对象，有一个唯一的关联的“对象引用”标定这个对象，并且对于任何类`C`，都有一个关联的对象引用类型`<span style="color:green;font-weight: bold;">ref</span>(C)`。这种类型的量，被称为由类`C`限定；它的值，要么是一个对象，要么是表示“没有对象”的特殊值`<span style="color:green;font-weight: bold;">none</span>`。

限定（qualification）将值的范围，限制至包括在限定类中的这些类的对象。不管何种限定，值的范围都包括值`none`。一个对象引用的限定，是一个类标识符，它因此服从声明的作用域和可见性 (去歧义)规则。限定关联于所提及的类声明所局部于的那个块的一个实例。这蕴涵着特定的关于限定有效性的检查，不能单独在程序正文的基础上进行，因此这种测试必须在程序执行期间完成。

引用是有限定的，蕴涵着一个给定引用所提及的对象，只能属于这个限定中提到的类，或属于这个限定类的子类。如果一个对象引用类型的限定，是另一个对象引用类型的限定类的子类，则前者被称为从属于后者。例如：

```
<tt><span style="color:green;font-weight: bold;">ref</span>(Order) next, previous;</tt>
```

对象表达式是获得对一个对象的引用的一种规则。对象表达式的值，是被引用的对象或`none`。运算符`:-`读作“指称”（denote），它指示将一个引用，指派（assign）到处在“引用指派符”左侧的引用类型的变量或过程标识符。例如：

```
next :- <span style="color:green;font-weight: bold;">new</span> Plate(50);
previous :- next;
next :- <span style="color:green;font-weight: bold;">none</span>;
```

而运算符`:=`读作“成为”（become），指示将一个值，指派到处在赋值符左侧的值类型的变量或过程标识符，或者将一个文本值，指派到在赋值符左侧者所引用的那个文本帧（frame）。

### 远程访问

一个对象的一个特定特性，由下列三项信息来完全标定（identify）：

- 这个对象。
- 外部于或等于这个对象的类的一个类。
- 在这个类中或在属于它的前缀序列的任何类中定义的一个特性标识符。

对于任何特性标定，第二项类信息都是在字面上定义的，从而形成静态绑定。属于其他对象的特性，可以要么单个的通过“远程标识符”（“点表示法”），要么成组的通过“连接”（connection）机制，从其外面“远程访问”来引用和使用。

远程标识符的形式为：`<small>简单对象表示式</small>.<small>特性标识符</small>`，它可用作简单变量、数组变量、函数指定式（designator）和过程语句中的标识符，这种点表示法给出对信息的单独片段的访问，例如：

```ObjectPascal
if next.number >= previous.number then ……;
```

它将名为`next`的类`Order`的对象的特性`number`，比较于名为`previous`的类`Order`的对象的特性`number`。

“成组访问”可通过连接语句`inspect`来完成，例如：

```
<tt><span style="color:green;font-weight: bold;">inspect</span> next <span style="color:green;font-weight: bold;">when</span> Plate <span style="color:green;font-weight: bold;">do begin</span> …… <span style="color:green;font-weight: bold;">end</span>;</tt>
<tt><span style="color:green;font-weight: bold;">inspect new</span> Plate(50) <span style="color:green;font-weight: bold;">do begin</span> …… <span style="color:green;font-weight: bold;">end</span>;</tt>
```

在这里的`begin`和`end`之间的语句中，`next`引用的对象所属的`Plate`类的所有信息片段，都可以直接提及。

在`do`后面的语句，如果是块以外的一个语句，它表现得也如同一个块。连接语句进而表现得如同被叫做“连接块”的第二个拟制（fictitious）的块所包围。连接机制的用途，是为在连接块中的特定特性标定，提供对象信息和在`when`子句中的类信息的隐式定义。

设对象表达式求值为`X`，在连接块执行期间，称对象`X`为“被连接上”了。连接块有一个关联的“块限定”，如果连接块有`when`子句，它是前面的类标识符，如果没有`when`子句，它是前面的对象表达式的限定。

### 虚过程

虚拟（virtual）量，是在类声明中由`<span style="color:green;font-weight: bold;">virtual:</span>`前导的量，它有双重用途：

- 允许在一个对象的一个前缀层级上，访问在内部前缀层级上声明的特性。
- 允许在一个前缀层级上的特性重新定义，在外部前缀层级上有效。

不同于一般的特性标定，虚过程形成“半动态绑定”，在某种意义上是类似传名调用的机制。这种机制不同于Smalltalk 80等面向对象编程语言中，上溯子类至超类的继承链来找到最近的方法实现的那种动态绑定。

一个对象的一个虚拟量，要么是“无匹配的”的，要么被标定为具有一个“匹配”特性，它是在这个虚拟量的前缀层级上或在内部前缀层级上声明的，其标识符重合（coincide）于虚拟量的标识符的一个特性。匹配特性必须与虚拟特性有相同的种类。在一个给定前缀层级上的匹配量的类型，必须重合或从属于这个虚拟规定的类型，和在任何外部前缀层级上声明的任何匹配量的类型。

在1986年修订的语言标准中，虚过程量可选的可使用`<span style="color:green;font-weight: bold;">procedure</span> <small><过程标识符></small> <small><过程规定></small>`方式来指定，即凭借它的类型，并且如果它有形式参数，则还凭借它们的类型、种类和传送模态。含有过程规定`<span style="color:green;font-weight: bold;">is</span> <small><过程声明></small>`的虚过程量，只能由有相同类型的过程来匹配，并它与这个过程规定具有相同的过程头部。

例如，下面实现散列表的`HashTable`类，声明了整数类型的虚过程`hash`并随即实现了它，又定义了要用到这个散列函数`hash`的查找过程`lookup`:

```ObjectPascal
class HashTable(n); integer n;
    virtual: integer procedure hash;
begin 
    integer procedure hash(t); text t;
    begin 
        ……
    end hash;
    text array table(0 : n-1); ……
    integer procedure lookup(t, old);
        name old; Boolean old; text t;
    begin
        integer i, istart; Boolean entered;
        i := istart := hash(t);
        while not entered do
        begin
            ……
        end;
        lookup := i;
    end lookup;
end HashTable;
HashTable class ALGOL_hashing;
begin
    integer procedure hash(t); text t;
    begin
        ……
    end hash;
end ALGOL_hashing;
```

作为子类的`ALGOL_hashing`类和以`HashTable`类为前缀的有前缀块，可以用自己的`hash`过程实现，替代在`HashTable`类中的实现。在子类的类主体和有前缀块的主体块中，能获得到过程`lookup`，而它用到的过程`hash`是这里提供的替代实现。

### 对象表达式

对象表达式 (计算机科学)，具有类型`<span style="color:green;font-weight: bold;">ref</span>(<small>限定</small>)`。对象表达式的限定规则包括：对象生成式（generator）、局部对象或限定对象，分别由跟随符号`new`、`this`或`qua`的标识符的类来限定（qualification）。

对象生成式new C，这里标定了类`C`，它涉及到属于类`C`的对象的生成和执行。这个对象是类`C`的一个新实例。对象生成式的求值构成自如下行动：

- 生成这个对象，并且如果这个对象生成式有实际参数，则求值它们，将得到的这些值及或引用传送给形式参数。
- 控制经过这个对象最初的`begin`进入其中，它籍此变成在“系附”状态下运行。当所生成的对象执行了`detach`基本过程变为“脱离”状态，或者经过这个对象最终的`end`退出而变为“终止”状态，对象生成式的求值完成。

局部对象this C，这里的`C`是类标识符。如果用在类`C`或`C`的任何子类的类主体中，或用在其块限定为类`C`或`C`的一个子类的连接块中，则这个对象表达式是有效的。在一个给定的上下文中，一个局部对象的值，是一个对象或连接到一个对象，它是这个对象表达式在其中有效的、最小的在字面上外在包围的块实例；如果没有这样的块，则这个对象表达式在这个上下文中是非法的。对于一个过程或类主体的实例（上下文），“在字面上外在包围”（textually enclosing）意为包含它的声明。

暂瞬限定`X <span style="color:green;font-weight: bold;">qua</span> C`，这里的`X`表示一个简单的引用表达式，而`C`是类标识符。设类`D`是对象`X`的限定，对于限定对象`X qua C`，如果类`C`外部于或等于类`D`，或者是`D`的一个子类，则这个对象表达式是合法的，否则是为非法。如果`X`的值是`none`，或者是属于外部于`C`的类的一个对象，则这个对象表达式求值形成一个运行时间错误；在其他情况下，`X qua C`的值，就是`X`的值。对一个串接的类对象的暂瞬限定，限制或扩展它的特性通过检视或远程访问的可见性。

对象关系表达式，使用运算符`is`和`in`，来测试一个对象的类成员关系。关系X is C，在`X`引用的是属于类`C`的对象之时，值为`true`，否则值为`false`。关系X in C，在`X`引用的是属于类`C`的对象，或者`X`是内部于类`C`的类之时，值为`true`，否则值为`false`。

不同于算术关系表达式使用比较运算符`=`和`<>`，对象引用关系表达式，使用比较运算符`==`和`=/=`，来比较引用，从而区别于对应的被引用的值。两个对象引用`X`和`Y`，在它们都引用相同的对象，或者它们都是`none`之时，被称为是“同一性 (面向对象编程)”的。关系`X == Y`，在这种情况下拥有的值为`true`，否则值为`false`。关系`X =/= Y`的值，是`X == Y`的值的否定。

### 作用域和可见性

不同于ALGOL 60规定了量的作用域与可见性 (去歧义)，在Simula 67中，一个标识符定义及其关联的标识符，在其作用域与可见性之间需要做出区别。

- 作用域：一个标识符定义的作用域，是它在其中可能有作用的那一部分程序正文。同一个标识符，可以定义在程序的很多地方，因此可以关联于不同的量。同一个标识符的这些定义的作用域，因而可能有所重叠，例如在一个标识符在内部块中被重新声明的情况下。
- 可见性 (去歧义)：一个标识符定义，如果它所关联的标识符，在程序的给定点上能够提及到这个定义所声明的量，则它被称为在这个点上是可见性 (去歧义)的。在给定标识符于此可见的程序正文的一个特定点上，最多只能有一个定义关联于这个标识符，例如在重新声明的情况下，在它们作用域的并集内任何给定点上，只有一个定义是可见的。

一个标识符定义的局部块，是在字面上最近的包围块，即子块、前缀块、过程主体或类主体，还包括围绕`for`语句的受控语句、过程声明、类声明和连接块等的拟制块。这个标识符和它的定义，被称为局部于这个块。

标识符定义，只在它们的作用域内是可见的。一个特定定义的可见性，在它的作用域内可以受到下列限制：

- 在一个标识符定义的局部块所包围的某个构造内，出现的具有相同标识符的标识符定义，是这个标识符的重新定义。在它们共同的作用域内，只有最内部的重新定义是可见的。
- 一个重新定义，出现在类的某个内部前缀层级。
- 远程访问，可以导致某些标识符定义在检视块或点表示法内变成不可见的。
- 使用`this`或`qua`，可以导致一个或多个重新定义被暂时停止。
- 在这个标识符定义所局部于的类声明中，这个标识符出现在由`hidden`及或`protected`前导的保护部分中。

一个标识符的重新定义，不允许出现在它的局部块的头部，这禁止了在同一个块中出现相同标识符的两个定义。

### 块实例

一个程序执行的各个组成部分，都是块即子块、有前缀块、连接块和类主体的动态实例。一个块实例，被称为“局部于”直接包含它的描述正文的那个块实例。例如一个给定类的实例，局部于包含这个类声明的块实例。最外层块的实例不局部于块实例。

在任何时间，“程序顺序控制”（Program Sequence Control），首字母简写为PSC，参照在一个块实例中当前被执行的程序点；形象的说，PSC“定位”至这个程序点，并被这个块实例所“包含”。进入任何块，都涉及到生成这个块的一个实例，于是PSC进入这个块实例，到达它的第一个可执行语句上。

一个块实例在任何时间，都是处在四种执行状态 (计算机科学)之一：系附（attached）、脱离（detached）、恢复（resumed）或终止（terminated）。没有在字面上给出前缀的任何类，都被定义为前缀了一个拟制（fictitious）的类，它只有一个特性即`detach`过程，因此所有类对象或有前缀块的实例，都拥有`detach`过程。在环境类`ENVIRONMENT`中，还定义了用于调度的`call`过程和`resume`过程。

- 一个非类块实例，总是在系附状态下，这个实例被称为系附到了导致它生成的那个块上。因此一个过程体的实例，系附到包含对应函数指定式或过程语句的块实例上。非类、非过程块实例，系附到它所局部的块实例。在PSC到达非类块实例的最终的`end`的时候，PSC返回到紧随导致这个块实例生成的语句或表达式的程序点。
- 一个对象，最初是在系附状态下，并被称为系附到包含对应对象生成语句的那个块实例上。一个对象，可以通过执行`detach`过程，进入脱离状态。通过执行`call`过程，可以使一个脱离状态的对象重新进入系附状态，它借此变为系附到包含这个调用语句的那个块实例上。通过执行`resume`过程，可以使一个脱离状态的对象进入恢复状态。不使用`detach`、`call`或`resume`过程的一个程序执行，是一个简单的系附状态的块的嵌套结构。
- 当PSC通过一个对象的最终`end`，或通过`goto`语句离开它的时候，这个对象进入终止状态。没有块实例系附到一个终止状态的类对象上。终止状态的对象，仍作为一个数据项而存在，它可以通过针对这个对象的包括过程和函数特性的这些特性的远程标定来引用。

每当一个块实例不复存在，局部于或系附于它的所有块实例也不复存在。一个对象的动态作用域，因而受限于它的类声明的作用域。在Simula 67最初标准中曾提到过，语言实现可以使用垃圾回收技术，来进一步缩减一个对象的有效寿命的跨度。一个数组声明的动态作用域，可以扩展超出包含这个声明的块实例的作用域，因为传引用调用参数传送模态适用于数组。

### 准并行系统

一个准并行系统，由“构件”（component）构成。构件是块实例组成的嵌套结构，其中标定（identify）这个构件的那个块实例，叫做“构件头领”。在每个系统中，构件中总是有一个被称呼为“主构件”，在Simula 67最初标准中它叫做“主程序”，其他构件叫做“对象构件”。

构件的定序（sequencing），由`detach`、`call`和`resume`过程支配。`detach`过程针对隐式指定的一个对象进行操作，而`call`和`resume`过程显式指定所操作的对象。

一个准并行系统，由包含局部的类声明的一个子块或有前缀块的任何实例所标定。标定了一个系统的块实例，叫做“系统头领”。一个系统的主构件的头领（head），重合（coincide）于系统头领。最外层的标定了一个系统的块实例，被称为“最外层系统”。

一个系统的主构件的头领，总是处在系附状态。一个系统的对象构件的头领，确切的就是局部于系统头领的那些脱离状态的或恢复状态的对象。在任何时间，在一个系统的构件之中，有确切的一个构件被称为“生效”（operative）的。不生效的构件，有关联的“重新激活点”，它标定在这个构件被激活（activate）时，要在它那里继续执行的程序点。一个对象构件是生效的，当且仅当这个构件的头领处在恢复状态。

称谓一个块实例`P`被一个块实例`X`“动态包围”，当且仅当存在一个块实例序列：`(P = Z<sub>0</sub>), Z<sub>1</sub>, ……, Z<sub>n-1</sub>, (Z<sub>n</sub> = X)` `(n >= 0)`，使得对于`i = 1, 2, ……, n`有着：`Z<sub>i-1</sub>`系附到`Z<sub>i</sub>`；或者`Z<sub>i-1</sub>`是一个恢复状态的对象，它关联的系统头领系附到`Z<sub>i</sub>`。终止状态和脱离状态的对象，不被除了自身之外的块实例动态包围。

将当前包含PSC的块实例动态包围起来的块实例链，叫做“运行链”。在运行链上的块实例，被称为是“运行”（operating）的，最外层的块实例总是运行的。一个构件只要其头领是运行的它就是运行的。

一个系统如果有一个构件是运行的，它就是运行的；在任何时间，一个系统最多有一个构件是运行的；运行的系统的头领，可以是不运行的。一个运行的构件总是生效的；如果一个系统的生效构件是不运行的，则这个系统也是不运行的。在不运行的系统中的生效的构件，是当这个系统成为不运行的时候在运行的构件，当这个系统再次成为运行的时候它将仍是运行的。

对于一个不生效的构件`C`，设它的头领是`X`，如果一个块实例`P`包含了它的重新激活点，则`P`被`X`动态包围，并且`P`除了自身之外不动态包围块实例。由构件头领`X`动态包围的这个块实例序列，被称为构件`C`的“重新激活链”。除了`X`之外，这个链上的所有构件头领，标定了生效而不运行的构件。在构件`C`成为运行的时候，在它的重新激活链上所有块也成为运行的。

除了系统构件，程序执行还可以包含不属于特定系统的“独立对象构件”。任何这种构件的头领，都是一个脱离状态的对象，它局部于一个类对象或一个过程主体实例，也就是说不局部于某个系统头领。根据定义，独立构件总是不生效的，只能对它执行`call`过程。

### 协程和生成器

准并行系统的`detach`/`resume`机制，是一种协程。在Simula 67最初标准中，没有恢复状态，并且没有规定`call`过程，但`call`过程通常会出现在当时的语言实现中。`detach`/`call`机制，相当于现在称为生成器的“半协程”，而最初标准中的`resume`过程可理解为`detach`过程与`call`过程的组合。1986年修订的语言标准，增补定义了`call`过程，新增了恢复状态，并且重新定义了`resume`过程，它不可再理解为`detach`过程与`call`过程的组合。

准并行系统，被创建于进入包含局部的类声明的一个子块或有前缀块的时候，借此生成的实例成为这个新系统的头领。初始时，这个主构件是生效的，并且是这个系统唯一的构件。

建立一个对象构件，要通过针对一个系附状态的对象，执行`detach`过程，借此PSC返回到这个对象系附到的那个块实例。这个对象进入脱离状态，并成为一个新的不生效的构件的头领，这个构件的重新激活点被定位到紧后于这个`detach`过程调用。如果这个对象局部于一个系统头领，则这个新的构件成为这个关联的系统的成员。

通过`detach`和`call`过程，可以形成“半对称定序”，这只涉及到对象构件，而不区分它们是属于系统的构件还是独立的构件。

- 对于一个不生效的对象构件，通过针对它的脱离状态的头领，执行一个`call`过程，可以重新激活这个构件，借此PSC移动到它的重新激活点上。这个头领重新进入系附状态，并变成系附到包含这个`call`过程调用的块实例上。这个构件正式的失去了本身（作为构件）的状态。

通过`detach`和`resume`过程，可以形成“对称构件定序”，这只涉及到属于一个准并行系统的那些构件。对立于半对称定序中的“调用者”与它的“被调用者”，在对称构件定序中的“恢复者”与它的“被恢复者”，具有完全的对称联系。

- 对于这个系统的一个不生效的对象构件，通过针对它的脱离状态的头领，执行一个`resume`过程，可以重新激活这个构件，借此PSC移动到它的重新激活点上；这个构件的头领进入恢复状态，而这个构件变成生效的。这个系统以前生效的构件变成不生效的，并且它重新激活点被定位到紧后于这个`resume`过程调用；如果这个构件是一个对象构件，则它的头领进入脱离状态。
- 对于当前生效的对象构件，通过针它的恢复状态的头领，执行一个`detach`过程调用，这个系统的主构件重获生效的状态，借此PSC移动回到主构件的重新激活点上。以前生效的构件变成不生效的，并且它重新激活点被定位到紧后于这个`detach`过程调用。这个构件的头领进入脱离状态。

PSC经过一个类对象的最终`end`的效果，除了使这个对象成为终止状态，而非脱离状态之外，相同于针对这个对象执行`detach`过程的效果。其结果是它不会得到重新激活点，并且在它已经拥有作为构件头领的状态时，失去这种状态。

## 程式范例

### Hello, World!

Simula 67下的经典Hello, World!范例：

```ObjectPascal
begin
    outtext("Hello, World!");
    outimage;
end;
```

`outtext`过程将字符串输出到缓冲区，而`outimage`过程将缓冲区内容输出到标准文件，二者都定义在输出文件类`OutFile`中，而它是文件类`File`的子类。

### 二叉树

下面例子中的`Tree`类，是二叉树数据结构实现：

```ObjectPascal
class Tree(val); integer val;
begin
    ref(Tree) left, right;
    procedure insert(x); integer x;
    begin
        if x < val then
        begin
            if left == none then
                left :- new Tree(x)
            else 
                left.insert(x)
        end
        else if right == none then
            right :- new Tree(x)
        else
            right.insert(x);
    end insert;  
    ref(Tree) procedure find(x); integer x;
    begin
        if x = val then
            this Tree
        else if x < val then
            (if left == none then
                none
             else 
                left.find(x))
        else if right == none then
            none
        else
            right.find(x);
    end find;
end Tree;
```

在`find`过程的主体中出现了表达式this Tree，它产生的值所引用的是当前节点。这里通过函数指定式`X.find(x)`，来调用对象`X`的`find`过程，如果`X.val = x`，则这个函数的结果是到`X`自身的引用值。

### 协程

Simula 67标准通过如下实例来诠释叫做“准并行系统”的协程机制：

```ObjectPascal
begin comment 系统S1;
    ref(C1) X1;
    class C1;
    begin
        procedure P1;
            detach;
        P1
    end C1;
    ref(C2) X2;
    class C2;
    begin
        procedure P2;
        begin
            detach; 
            ! 可尝试detach或resume(X1);
        end P2;
        begin comment 系统S2;
            ref(C3) X3;
            class C3;
            begin 
                detach;
                P2
            end C3;
            X3 :- new C3;
            resume(X3)
        end S2
    end C2;
    X1 :- new C1;
    X2 :- new C2;
    call(X2); ! 可尝试resume(X2);
end S1;
```

这个例子程序中，有两个准并行系统`S1`和`S2`。系统`S1`是对应最外层子块的最外层系统，它包含了两个类声明`C1`和`C2`。系统`S2`对应于类`C2`的类主体中的匿名块，它包含了一个类声明`C3`。

在PSC进入最外层子块开始处，产生系统`S1`的系统头领，到达第28行的系统`S1`主构件的第一个可执行语句，开始生成对象`X1`，进入类`C1`，PSC到达第7行的类主体的第一个可执行语句，调用了类`C1`的特性过程`P1`，进入第6行的过程体的第一个可执行语句，这时的状况是：

```
['''S1'''] ← (X1) ← (P1) ← <span style="color:FireBrick;">PSC</span>
```

这里方括号表示系统头领，圆括号表示其他种类的块实例，左向箭头表示系附，使用粗体表示这个块实例执行了对象生成式。执行第6行的`detach`语句后，对象构件头领`X1`进入脱离状态，保存`X1`的重新激活点为第6行`P1`过程体结束，回溯到第28行对象产生式结束，系统`S1`的对象`X1`生成完毕，这时的情况是：

```
[S1] ← <span style="color:FireBrick;">PSC</span>
 |
(X1) ← (P1) ← <X1重新激活点>
```

这里的竖杠将对象构件头领列为一个系统的成员。PSC到达系统`S1`主构件第29行，开始生成对象`X2`，进入类`C2`，PSC到达第17行的类主体中匿名块开始处，进入这个子块产生系统`S2`的系统头领，PSC到达第24行的系统`S2`主构件的第一个可执行语句，开始生成对象`X3`，进入类`C3`，PSC到达第20行类主体开始处，这时的情况是：

```
['''S1'''] ← (X2) ← ['''S2'''] ← (X3) ← <span style="color:FireBrick;">PSC</span>
 |
(X1) ← (P1) ← <X1重新激活点>
```

执行第21行类主体的第一个可执行语句`detach`后，对象构件头领`X3`进入脱离状态，保存`X3`的重新激活点为第22行，回溯到第24行对象产生式结束，系统`S2`的对象`X3`生成完毕，这时的情况是：

```
['''S1'''] ← (X2) ← [S2] ← <span style="color:FireBrick;">PSC</span>
 |             |
 |            (X3) ← <X3重新激活点>
 |
(X1) ← (P1) ← <X1重新激活点>
```

PSC到达系统`S2`主构件的第25行，执行`resume(X3)`语句后，保存系统`S2`主构件的重新激活点为第26行，对象构件头领`X3`进入恢复状态，PSC恢复到第22行的对象`X3`的类主体之中，这时的情况是：

```
['''S1'''] ← (X2) ← [S2] ← <S2重新激活点>
 |             |
 |            (<u>X3</u>) ← <span style="color:FireBrick;">PSC</span>
 |
(X1) ← (P1) ← <X1重新激活点>
```

这里的下划线指示这个块实例处在恢复状态。执行第22行的类`C2`的特性过程`P2`，到达在第14行的过程体的第一个可执行语句，这时的情况如下，并标记为“状况A”：

```
                                  <span style="color:DarkCyan;">! 状况A ;</span>
['''S1'''] ← (X2) ← [S2] ← <S2重新激活点>
 |             |
 |            (<u>X3</u>) ← (P2) ← <span style="color:FireBrick;">PSC</span>
 |
(X1) ← (P1) ← <X1重新激活点>
```

执行在第14行`P2`过程体的`detach`语句后，对象构件头领`X2`进入脱离状态，保存`X2`的重新激活点为第15行，回溯到第29行对象产生式结束，系统`S1`的对象`X2`生成完毕，这时的情况如下，并标记为“状况B”：

```
                                  <span style="color:DarkCyan;">！状况B ;</span>
[S1] ← <span style="color:FireBrick;">PSC</span>
 |
(X1) ← (P1) ← <X1重新激活点>
 |
(X2) ← [S2] ← <S2重新激活点>
        |
       (<u>X3</u>) ← (P2) ← <X2重新激活点>
```

注意对象`X3`仍是系统`S2`的生效构件，它并没有自己的重新激活点。序列`(P2, X3, X2)`是`X2`的重新激活链，这里的`X3`是恢复状态，而它的系统头领`S2`系附到了`X2`。

PSC进入系统`S1`主构件第30行，执行`call(X2)`语句后，对象构件头领`X2`进入系附状态，PSC恢复为第15行的`P2`过程体之中，这时的情况重现为前面的“状况A”（`S1`不加粗体）。

如果将系统`S1`主构件中第30行改写为一个`resume(X2)`，执行后对象构件头领`X2`进入恢复状态，保存系统`S1`主构件的重新激活点为第31行，PSC恢复为第15行的`P2`过程体之中，会出现如下情况：

```
[S1] ← <S1重新激活点>
 |
(X1) ← (P1) ← <X1重新激活点>
 |
(<u>X2</u>) ← [S2] ← <S2重新激活点>
        |
       (<u>X3</u>) ← (P2) ← <span style="color:FireBrick;">PSC</span>
```

序列`(P2, X3, X2)`是`X2`的运行链。

如果将`P2`过程体中第15行的注释替换为一个`detach`语句，执行后对象构件头领`X2`进入脱离状态，保存`X2`的重新激活点是第16行，PSC恢复到第31行的系统`S1`主构件之中，这时的情况重现为前面的“状况B”。

如果将`P2`中第15行的注释替换为一个`resume(X1)`语句，执行后对象构件头领`X2`进入脱离状态，对象构件头领`X1`进入恢复状态，保存`X2`的重新激活点为第16行，PSC恢复到第6行的`P1`过程体的过程结束，会出现如下情况：

```
[S1] ← <S1重新激活点>
 |
(<u>X1</u>) ← (P1) ← <span style="color:FireBrick;">PSC</span>
 |
(X2) ← [S2] ← <S2重新激活点>
        |
       (<u>X3</u>) ← (P2) ← <X2重新激活点>
```

### 抽象类

下面例子中，定义了一个字形类`Glyph`，它是抽象基础类，并且有二个实现子类：字符类`Char`和行 (文本文件)类`Line`：

```ObjectPascal
begin
    class Glyph;
        virtual: procedure print is procedure print;
    begin
    end;
    Glyph class Char(c);
        character c;
    begin
        procedure print;
            outchar(c);
    end;
    Glyph class Line(cs);
        ref(Glyph) array cs;
    begin
        procedure print;
        begin
            integer i;
            for i := 1 step 1 until upperbound(cs, 1) do
                cs(i).print;
            outimage;
        end;
    end;
    ref(Glyph) rg;
    ref(Glyph) array rgs(1 : 4);
    rgs(1) :- new Char('A');
    rgs(2) :- new Char('b');
    rgs(3) :- new Char('b');
    rgs(4) :- new Char('a');
    rg :- new Line(rgs);
    rg.print;
end;
```

这里的虚过程量`print`具有过程规定`is procedure print`，它匹配既没有形式参数也没有结果值的`print`过程，如果不加以这种过程规定，则它可以匹配具有任何形式参数和任何结果值的`print`过程。在Simula 67中，没有不可以实例化带有纯虚过程类的特定限制，因而缺乏真正的抽象类的概念，所有类都可以被实例化，但是调用纯虚过程会产生运行时间错误。

### 模拟器

在下面的离散事件模拟例子中，Sam、Sally和Andy正在逛商店买衣服，他们必须共享一个试衣间。他们每人只能浏览商店大约12分钟，并接着独占的使用试衣间大约3分钟，每个行动都服从正态分布，他们的试衣间经历被模拟如下：

```ObjectPascal
Simulation
begin
    class FittingRoom;
    begin
        ref(Head) door;
        Boolean inUse;
        procedure request;
        begin
            if inUse then
            begin
                wait(door);
                door.first.out;
            end;
            inUse := true;
        end;
        procedure leave;
        begin
            inUse := false;
            activate door.first;
        end;
        door :- new Head;
    end;
    procedure report(message); text message;
    begin
        outfix(time, 2, 0);
        outtext(": " & message);
        outimage;
    end;
    Process class Person(pname); text pname;
    begin
        while true do
        begin
            hold(normal(12, 4, u));
            report(pname & " 要求用试衣间");
            fittingroom1.request;
            report(pname & " 已进入试衣间");
            hold(Normal(3, 1, u));
            fittingroom1.leave;
            report(pname & " 已离开试衣间");
        end;
    end;
    integer u;
    ref(FittingRoom) fittingRoom1;
    fittingRoom1 :- new FittingRoom;
    activate new Person("Sam");
    activate new Person("Sally");
    activate new Person("Andy");
    hold(100);
end;
```

主程序是前缀着模拟器类`Simulation`的有前缀块。模拟器类可在任何块上使用，而且模拟器甚至可以嵌套，比如在模拟某人做模拟的时候。时间过程`time`、等待过程`wait`、保持过程`hold`和激活过程`ACTIVAT`，定义在`Simulation`类之中。

进程类`Process`是`Simulation`类的嵌套类。激活语句`activate`，只有处在`Simulation`类所包含的类的对象之中，或处在其前缀部分是这种对象的有前缀块之中，才是有效的。激活语句的作用，被定义为得到调用激活过程`ACTIVAT`的那种效果。

集合类`Simset`是`Simulation`类的前缀类，其中定义了三个嵌套类：表示集合整体的`Head`类，表示集合元素的`Link`类，和二者的前缀类链表类`Linkage`。首位过程`first`，定义在`Head`类中；退出过程`out`和在等待过程`wait`中用到的进入过程`into`，定义在`Link`类中。

在试衣间类`FittingRoom`中，为了让人们排队等待访问试衣间，使用了门对象`door`，它是队列类即`Head`类的一个对象。试衣间类定义了两个过程：要求过程`request`：如果试衣间中正有人使用，则他必须等待于门队列之中，即`wait(door)`；当他可以使用试衣间之时，相应的从门队列中移除自己，即`door.first.out`。离开过程`leave`：如果门队列中有人等待的话，放行其中第一个人，即`activate door.first`。

个人类`Person`是`Process`类的子类，它的行动是浏览商店和在试衣间试衣，使用保持过程`hold`来描述其所用时间，并调用对应的试衣间对象的过程来要求和离开试衣间。正态分布的随机抽签过程`normal`，定义在环境类`ENVIRONMENT`中，它的最后的参数，必须是指定一个伪随机数串流的一个整数。

主程序是进程类`Process`的子类`MAIN_PROGRAM`类的对象。它建立`FittingRoom`类的实例`fittingRoom1`对象，接着建立并激活`Person`类的三个对象，从而将他们三人放置入事件队列之中，主程序在终止前保持100分钟的模拟时间。

## 基本资料

- 编程范型：多范型: 指令式, 过程式, 结构化, 物件导向, 并发
- 发行时间：1962年（Simula I）、1967年（Simula 67）
- 设计者：奥利-约翰·达尔
- 开发者：克利斯登·奈加特
- 软件版本周期：Standard SIMULA
- 软件版本周期：1986年8月25日
- 类型系统：静态、名称式类型系统
- 作用域：词法
- 实作语言：主要为ALGOL 60（有一些Simscript成分）
- 软件实现：Portable Simula Revisited, GNU Cim
- 受影响于：ALGOL 60, Simscript
- 施影响于：Smalltalk、CLU、C++、BETA、Object Pascal、Modula-3、Java
- 操作系统：类Unix系统、Windows、z/OS、TOPS-10、MVS