Simula
![本页使用了标题或全文手工转换](http://images.weserv.nl/?url=//upload.wikimedia.org/wikipedia/commons/thumb/c/cd/Zh_conversion_icon_m.svg/35px-Zh_conversion_icon_m.svg.png)
![]() | |
编程范型 | 多范型: 指令式, 过程式, 结构化, 面向对象, 并发 |
---|---|
设计者 | 奥利-约翰·达尔 |
实现者 | 克利斯登·奈加特 |
发行时间 | 1962年 1967年 (Simula 67) | (Simula I)
当前版本 |
|
类型系统 | 静态、名称式 |
作用域 | 词法 |
实现语言 | 主要为ALGOL 60(有一些Simscript成分) |
操作系统 | 类Unix系统、Windows、z/OS、TOPS-10、MVS |
主要实现产品 | |
Portable Simula Revisited[2], GNU Cim[3] | |
启发语言 | |
ALGOL 60, Simscript[4] | |
影响语言 | |
Smalltalk[5]、CLU[6]、C++、BETA、Object Pascal、Modula-3、Java |
Simula,一种编译式的编程语言,由奥利-约翰·达尔与克利斯登·奈加特,在1960年代于挪威奥斯陆的挪威计算中心,开发出来了Simula I与Simula 67两代。它承继了ALGOL 60作为基础,被认为是第一个面向对象编程的编程语言。
Simula 67介入了对象、类、子类(后来惯称为子类继承超类)、虚过程[8],还有协程、离散事件模拟和特征性的垃圾收集[9]。Simula的影响经常被低估[10],Smalltalk[5]、CLU[6]、C++、Object Pascal、Modula-3、Java和后来的很多编程语言,受到了Simula 67的启发。BETA是Simula的现代后继者。
历史
![](http://images.weserv.nl/?url=//upload.wikimedia.org/wikipedia/commons/thumb/f/f0/Simula_-_discrete-event_network.en.svg/400px-Simula_-_discrete-event_network.en.svg.png)
1957年,克利斯登·奈加特开始在NDRE的Ferranti Mercury电脑上写模拟器,为此他需要更强大的编程语言。1962年1月,奥利-约翰·达尔开始跟他合作,他们受到ALGOL 60的启发。1962年5月,发展出第一份模拟器编程语言提议,语言取名为SIMULA,后改称为Simula I。此时,克利斯登·奈加特受到Sperry Rand公司邀请,去协助他们开发UNIVAC 1107电脑。UNIVAC软件部门的主管鲍伯·贝莫,力邀克利斯登·奈加特前往国际资讯处理协会(IFIP)举行的第二次国际会议上,发表了论文“SIMULA-扩展ALGOL到离散事件网络的描述”[11]。
1963年8月,挪威计算中心(NCC)购买到UNIVAC 1107,在UNIVAC的合约同意下,奥利-约翰·达尔在这台电脑上安装以凯斯西储 1107 ALGOL 60的编译器来实现的Simula I。1965年1月,Simula I终于可以在UNIVAC 1107上完全的运作。Simula I在1968年被移植到了Burroughs B5500电脑,以及后来苏联的URAL-16电脑。
1965年,东尼·霍尔首次提出“记录类”(record class)构造的概念[12],1966年,克利斯登·奈加特与奥利-约翰·达尔二人,将Simula I的具有“准并行”性质的“进程”,扩展成了具有了记录类性质的广义进程,随即改称为“物件”[13]。1967年5月,奈加特和达尔在奥斯陆举办的IFIP工作小组论坛中,发表了关于类与子类声明的论文,形成Simula 67的第一份定义文件[14]。
1968年召开的会议,组成了SIMULA标准小组,并发表了第一份官方Simula标准文件“SIMULA 67通用基础语言”[15]。在1960年代后期和1970年代早期,Simula 67主要实现于四个系统之上:挪威计算中心的UNIVAC 1100系列,挪威计算中心的IBM System/360和System/370,奥斯陆大学在Kjeller联合安装的CDC 3000系列,和瑞典国防研究所(FOA)的DEC TOPS-10。
当前的Simula 67业界标准,是在1986年修订的“标准SIMULA”[7],它在1987年被接受为瑞典国家标准[16]。它有四个主要实现:Simula AS、Lund Simula、GNU Cim[3]和Portable Simula Revisited[2]。
Simula影响了Smalltalk[5],和后来的面向对象编程语言[10]。C++语言和Java语言的创始人,都认可自己受到了Simula的重要影响[17]。
语法和语义
Simula 67包含通用算法语言ALGOL 60的多数特征作为自己的子集[7],它是大小写不敏感的。Simula 67的中心概念是对象,对象是自我容纳(self-contained)的一段程序即一个块实例,它拥有由一个类声明定义的自己的局部数据和行动(action)。类是一种过程[18],它有能力引起在它被调用后仍存活的一个块实例,而这些实例就叫做这个类的对象。为了操纵对象和相互关联对象,语言介入了链表处理设施。
Simula 67为了将整个程序执行组织为:对象所属的诸“行动阶段”的一个序列,而将其所必需的基本功能特征,包含在作为“系统类”的“环境类”之中。在环境类中有被称为“标准系统类”的模拟器类Simulation
,它定义了充当系统时间轴的“测序集合”(sequencing set)对象SQS
、进程类Process
和事件通告类EVENT_NOTICE
,测序集合的成员是事件通告对象,它通过其PROC
特性提及一个进程对象;事件通告表示了对应这个进程对象的下一个行动阶段的一个事件,被调度在系统时间EVTIME
时发生。
块
块是成序列的声明,跟随着成序列的语句,并被包围在关键字begin
和end
之间。块自身是一种语句[19],在Simula 67中,它包括子块和有前缀(prefixed)块,在语法上,子块就是无前缀的ALGOL 60的块,即匿名块和过程主体。
声明担负定义在程序中用到的量(quantity)的特定属性,并给它们关联上标识符,这包括:简单变量、数组、switch
(标签列表)、过程、类和外来声明。块中的每个语句,都可以是块或复合语句。复合语句与块相比在语法上没有声明。
所有的块,自动地介入命名法(nomenclature)的一个新的层级:在这个块内出现的标识符,可以通过合适的声明,而被指定为局部于所论及的这个块;这个标识符在这个块里面的所表示的实体,不存在于它的外面;这个标识符在这个块外面的所表示的任何实体,在这个块里面是不可见的;在Simula 67中,可通过连接或远程访问使它成为可见的。
除了表示标签的标识符之外,一个标识符,如果出现在一个块中,而且并非声明于这个块中,则它非局部于这个块。因为块中的语句自身可以是一个块,局部和非局部于一个块的概念,必须递归地去理解。
块是一种形式描述或模式,关乎汇聚的数据结构和关联的算法以及行动[20]。当一个块执行的时候,生成这个块的一个动态实例[21]。一个块实例,可以被认为是它的形式描述的文字复本。在电脑中,一个块实例可以采用某一种形式的内存区域,它包含需要的动态块资讯,并包括空间来持有局部于这个块的变量的内容[22]。
块的任何一个内部的块,仍然是一个模式,但是在其中出现的非局部标识符,标定了局部于在字面上外在包围(textually enclosing)的块实例的项目[23]。非局部于内部的块的标识符绑定,对这个内部的块的任何随后的动态实例,保持有效[24]。
块实例中的局部变量,标识了分配给块实例的内存片段。在进入一个块之中的时候,任何局部于这个块即在其中声明的变量,都会被初始化。局部于一个块的一个变量,是一个内存装置,其内容依据这个变量的类型,要么是一个值,要么是一个引用。
值类型可特征化为直接关联着一个可能值的集合,即这个类型的“值范围”。引用概念,对应于一个名字或一个“指针”的直观想法。在Simula 67中有两种引用类型,对象引用类型和文本引用。对于值类型不关联着引用概念。提供了引用值的机制,还反映了机器的寻址可能性;在特定简单情况下,一个引用可以被实现为一个存储的值的内存地址。
过程
过程与块相比,它有一个名字,可以在程序的一些不同地方提及,并且在调用时可以给出参数[25]。过程和块都不可能建立到它或它内部的引用[25];对于一个给定块,有可能生成它的一些可以共存和交互的实例[22],例如递归过程的实例。
过程的参数传送模态,除了有传值调用和传名调用[26],在Simula 67中,又增加了传引用调用。过程的缺省的传送模态,在Simula 67中,对于值类型的参数是传值调用,对于所有其他类型的参数是传引用调用;故而在过程声明的参数规定中,增加了以name
为前导的名字部分,用来指定所述及的参数采用传名调用。
在过程主体内传名调用的形式参数的每次出现,都引起对实际参数的一次求值。在Simula 67中,这个求值发生在过程语句的上下文中,就是说不会出现标识符冲突,因为过程主体和它的变量此时是不可见的。过程调用的执行,在有参数的情况下要经历如下步骤:建立形式参数块实例;求值对应于传值调用或传引用调用的实际参数,并将其结果赋值给形式参数块实例的对应变量;过程主体被初始化并开始执行。
真正(proper)过程,不定义特定类型的函数指定式的值,在Simula 67中,它被称为具有普遍(universal)类型,任何类型都从属(subordinate)于普遍类型。
类与对象
类声明定义一个程序(数据和行动)模式,而符合(conforming)这个模式的对象被称为“属于相同的类”。不同于Smalltalk 80等面向对象编程语言,Simula 67的类和后来的Modula-3的对象类型,不进行实例变量与类变量的区分。
对于一个给定对象,类声明中形式参数、在虚拟部分中规定的虚拟量,和声明为局部于类主体(body)的量,叫做这个对象的特性(attribute)。一个特性的声明或规定叫做特性定义。在1986年修订的语言标准中,通过保护规定可以限制类特性标识符的可见性。
类声明中的形式参数,不适用传名调用,这是为了保证实际参数在类所引起的对象存活期间不能消失[27]。在规定部分中需要对每个形式参数进行规定,这些参数被当作局部于类主体的变量。类主体通常是一个块,即使它如语法形式所允许的那样是块以外的一个语句,也表现得如同一个块。一个分裂(split)主体表现为一个块,在其中符号inner
表示一个虚设(dummy)语句。
Simula 67的类主体中除了有定义特性的声明,还可以有定义行动的语句。如果在一个类声明中有行动定义,则符合这个模式的行动,可以由属于这个类的所有对象执行。如果在类声明中没有指定行动,则定义了一类纯数据结构,很多后来的面向对象编程语言就是这样,将对象作为记录或结构的扩展。例如:
class Order(number); integer number;
begin
integer numberOfUnits, arrivalDate;
real processingTime;
end;
属于特定类的对象,通过对象表达式中的对象生成式new
来生成,并制作出到它的引用。例如,想要多少就能生成多少属于这个Order
类的一个新对象:
new Order(103);
属于一个对象的行动,可以都在就一个过程而言的一个序列中执行。这些行动,还可以作为一系列的独立子序列或“行动阶段”(active phase)来执行。在一个给定对象的两个行动阶段之间,可以出现任何数目的其他对象的行动阶段。
子类与类串接
一个类可以被用作到另一个类声明的“前缀”,从而将前缀所定义的性质,建造入这个新的类声明定义的对象之中。具有前缀类C
和类标识符D
的一个类声明,定义了类C
的一个子类D
。属于子类D
的对象,由自身是类C
的对象的“前缀部分”,和类D
的类声明主体所描述的“主体部分”组成。这两部分串接而形成了一个复合对象,它可以用串接成的类声明来正式的描述,串接的过程被认为先于程序执行而发生。
类只能在定义它的块层级中用作前缀。前缀类自身也可以有前缀。类不能出现在自己的前缀序列中。子类被称为内部(inner)于前缀类,而前缀类被称为外部(outer)于子类,但不可将子类称为前缀类的内部类。这不同于后来的面向对象编程语言中的称谓:“派生类”扩展“基础类”,或“子类”继承“超类”。例如:
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
之前。
设Cn
是具有前缀序列C1, C2, ……, Cn-1
的一个类,这里类Ck
(k = 1, 2, ……, n)
的下标k
叫做前缀层级,X
是属于类Cn
的一个对象,则X
是一个复合对象;非正式的说,串接机制有如下结果:
- 对象
X
拥有的特性集合,是在序列C1, C2, ……, Cn
中诸类所定义特性集合的并集。在类Ck
(1 <= k <= n)
中定义的特性,被称为定义在前缀层级k
。 - 对象
X
拥有的“运算规则”,由来自这些类主体的诸语句构成,它们所属的前缀层级遵循规定的次序。来自类Ck
的语句,被称为属于对象X
的前缀层级k
。 - 在对象
X
的前缀层级k
的语句,能访问在对象X
的等于或外部于k
的前缀层级上定义的它的所有特性,但是如果外部的类Ci
(i < k)
中存在冲突定义,则导致对应特性不可见而不能直接访问。这些不可见特性仍有访问方式,例如通过使用过程或this Ci
。 - 在对象
X
的前缀层级k
的语句,不能立即访问在对象X
的内部于k
的前缀层级上定义的它的那些特性,除非通过虚拟量。 - 在前缀层级
k
的分裂主体中,符号inner
,表示对象X
的属于内部于k
的前缀层级的运算规则的诸语句,或者在k = n
时表示虚设语句。如果序列C1, ……, Cn-1
都没有分裂主体,则在对象X
的运算规则中诸语句所属的前缀层级遵循递增次序。
用作前缀的系统类,与其前缀链的所有的类一起,被当作声明于最小的包围其字面上出现的块中。因此重新声明可以出现在一个程序的内部的块层级中。
有前缀块的一个实例,是由块前缀生成的一个单一的对象,与这个主体块的实例,串接而成一个复合对象,其串接定义采用类似于类的串接规则。块前缀的类的形式参数,按块前缀的实际参数所示初始化。对有前缀块有两个限制:一个类如果在其中通过使用this
来引用类自身,则它作为块前缀是非法的。块前缀的类标识符所提及的类,必须局部于最小的包围这个有前缀块的块。
对象引用
当属于各种类的很多对象,作为同一个程序的各个部分而共存的时候,需要能够对个体对象指定名字,为此介入了叫做“引用”的新的基本类型;还要能相互关联对象,比如通过二叉树和各种其他类型的列表结构。Simula 67将循环双向链表叫做“集合”,它将集合类Simset
作为“标准系统类”介入为环境类的一部分。
对于某个类的一个对象,有一个唯一的关联的“对象引用”标定这个对象,并且对于任何类C
,都有一个关联的对象引用类型ref(C)
。这种类型的量,被称为由类C
限定;它的值,要么是一个对象,要么是表示“没有对象”的特殊值none
。
限定(qualification)将值的范围,限制至包括在限定类中的这些类的对象。不管何种限定,值的范围都包括值none
。一个对象引用的限定,是一个类标识符,它因此服从声明的作用域和可见性规则。限定关联于所提及的类声明所局部于的那个块的一个实例。这蕴涵着特定的关于限定有效性的检查,不能单独在程序正文的基础上进行,因此这种测试必须在程序执行期间完成。
引用是有限定的,蕴涵着一个给定引用所提及的对象,只能属于这个限定中提到的类,或属于这个限定类的子类。如果一个对象引用类型的限定,是另一个对象引用类型的限定类的子类,则前者被称为从属于后者。例如:
ref(Order) next, previous;
对象表达式是获得对一个对象的引用的一种规则。对象表达式的值,是被引用的对象或none
。运算符:-
读作“指称”(denote),它指示将一个引用,指派(assign)到处在“引用指派符”左侧的引用类型的变量或过程标识符。例如:
next :- new Plate(50); previous :- next; next :- none;
而运算符:=
读作“成为”(become),指示将一个值,指派到处在赋值符左侧的值类型的变量或过程标识符,或者将一个文本值,指派到在赋值符左侧者所引用的那个文本帧(frame)。
远程访问
一个对象的一个特定特性,由下列三项资讯来完全标定(identify):
对于任何特性标定,第二项类资讯都是在字面上定义的,从而形成静态绑定[28]。属于其他对象的特性,可以要么单个的通过“远程标识符”(“点表示法”),要么成组的通过“连接”(connection)机制,从其外面“远程访问”来引用和使用。
远程标识符的形式为:简单对象表示式.特性标识符
,它可用作简单变量、数组变量、函数指定式(designator)和过程语句中的标识符,这种点表示法给出对资讯的单独片段的访问,例如:
if next.number >= previous.number then ……;
它将名为next
的类Order
的对象的特性number
,比较于名为previous
的类Order
的对象的特性number
。
“成组访问”可通过连接语句inspect
来完成,例如:
inspect next when Plate do begin …… end; inspect new Plate(50) do begin …… end;
在这里的begin
和end
之间的语句中,next
引用的对象所属的Plate
类的所有资讯片段,都可以直接提及。
在do
后面的语句,如果是块以外的一个语句,它表现得也如同一个块。连接语句进而表现得如同被叫做“连接块”的第二个虚构(fictitious)的块所包围。连接机制的用途,是为在连接块中的特定特性标定,提供对象资讯和在when
子句中的类资讯的隐式定义。
设对象表达式求值为X
,在连接块执行期间,称对象X
为“被连接上”了。连接块有一个关联的“块限定”,如果连接块有when
子句,它是前面的类标识符,如果没有when
子句,它是前面的对象表达式的限定。
虚过程
虚拟(virtual)量,是在类声明中由virtual:
前导的量,它有双重用途:
- 允许在一个对象的一个前缀层级上,访问在内部前缀层级上声明的特性。
- 允许在一个前缀层级上的特性重新定义,在外部前缀层级上有效。
不同于一般的特性标定,虚过程形成“半动态绑定”[8],在某种意义上是类似传名调用的机制[27]。这种机制不同于Smalltalk 80等面向对象编程语言中,上溯子类至超类的继承链来找到最近的方法实现的那种动态绑定。
一个对象的一个虚拟量,要么是“无匹配的”的,要么被标定为具有一个“匹配”特性,它是在这个虚拟量的前缀层级上或在内部前缀层级上声明的,其标识符重合(coincide)于虚拟量的标识符的一个特性。匹配特性必须与虚拟特性有相同的种类。在一个给定前缀层级上的匹配量的类型,必须重合或从属于这个虚拟规定的类型,和在任何外部前缀层级上声明的任何匹配量的类型。
在1986年修订的语言标准中,虚过程量可选的可使用procedure <过程标识符> <过程规定>
方式来指定,即凭借它的类型,并且如果它有形式参数,则还凭借它们的类型、种类和传送模态。含有过程规定is <过程声明>
的虚过程量,只能由有相同类型的过程来匹配,并它与这个过程规定具有相同的过程头部。
例如,下面实现散列表的HashTable
类,声明了整数类型的虚过程hash
并随即实现了它,又定义了要用到这个散列函数hash
的查找过程lookup
:
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
是这里提供的替代实现。
对象表达式
对象表达式,具有类型ref(限定)
。对象表达式的限定规则包括:对象生成式(generator)、局部对象或限定对象,分别由跟随符号new
、this
或qua
的标识符的类来限定(qualification)。
对象生成式new C
,这里标定了类C
,它涉及到属于类C
的对象的生成和执行。这个对象是类C
的一个新实例。对象生成式的求值构成自如下行动:
- 生成这个对象,并且如果这个对象生成式有实际参数,则求值它们,将得到的这些值及或引用传送给形式参数。
- 控制经过这个对象最初的
begin
进入其中,它籍此变成在“系附”状态下运行。当所生成的对象执行了detach
基本过程变为“脱离”状态,或者经过这个对象最终的end
退出而变为“终止”状态,对象生成式的求值完成。
局部对象this C
,这里的C
是类标识符。如果用在类C
或C
的任何子类的类主体中,或用在其块限定为类C
或C
的一个子类的连接块中,则这个对象表达式是有效的。在一个给定的上下文中,一个局部对象的值,是一个对象或连接到一个对象,它是这个对象表达式在其中有效的、最小的在字面上外在包围的块实例;如果没有这样的块,则这个对象表达式在这个上下文中是非法的。对于一个过程或类主体的实例(上下文),“在字面上外在包围”(textually enclosing)意为包含它的声明。
暂瞬限定X qua 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最初标准中曾提到过,语言实现可以使用垃圾回收技术[9],来进一步缩减一个对象的有效寿命的跨度。一个数组声明的动态作用域,可以扩展超出包含这个声明的块实例的作用域,因为传引用调用参数传送模态适用于数组。
准并行系统
一个准并行系统[29],由“构件”(component)构成。构件是块实例组成的嵌套结构,其中标定(identify)这个构件的那个块实例,叫做“构件头领”。在每个系统中,构件中总是有一个被称呼为“主构件”,在Simula 67最初标准中它叫做“主程序”,其他构件叫做“对象构件”。
构件的测序(sequencing),由detach
、call
和resume
过程支配。detach
过程针对隐式指定的一个对象进行操作,而call
和resume
过程显式指定所操作的对象。
一个准并行系统,由包含局部的类声明的一个子块或有前缀块的任何实例所标定。标定了一个系统的块实例,叫做“系统头领”。一个系统的主构件的头领(head),重合(coincide)于系统头领。最外层的标定了一个系统的块实例,被称为“最外层系统”。
一个系统的主构件的头领,总是处在系附状态。一个系统的对象构件的头领,确切的就是局部于系统头领的那些脱离状态的或恢复状态的对象。在任何时间,在一个系统的构件之中,有确切的一个构件被称为“生效”(operative)的。不生效的构件,有关联的“重新激活点”,它标定在这个构件被激活(activate)时,要在它那里继续执行的程序点。一个对象构件是生效的,当且仅当这个构件的头领处在恢复状态。
称谓一个块实例P
被一个块实例X
“动态包围”,当且仅当存在一个块实例序列:(P = Z0), Z1, ……, Zn-1, (Zn = X)
(n >= 0)
,使得对于i = 1, 2, ……, n
有着:Zi-1
系附到Zi
;或者Zi-1
是一个恢复状态的对象,它关联的系统头领系附到Zi
。终止状态和脱离状态的对象,不被除了自身之外的块实例动态包围。
将当前包含PSC的块实例动态包围起来的块实例链,叫做“运行链”。在运行链上的块实例,被称为是“运行”(operating)的,最外层的块实例总是运行的。一个构件只要其头领是运行的它就是运行的。
一个系统如果有一个构件是运行的,它就是运行的;在任何时间,一个系统最多有一个构件是运行的;运行的系统的头领,可以是不运行的。一个运行的构件总是生效的;如果一个系统的生效构件是不运行的,则这个系统也是不运行的。在不运行的系统中的生效的构件,是当这个系统成为不运行的时候在运行的构件,当这个系统再次成为运行的时候它将仍是运行的。
对于一个不生效的构件C
,设它的头领是X
,如果一个块实例P
包含了它的重新激活点,则P
被X
动态包围,并且P
除了自身之外不动态包围块实例。由构件头领X
动态包围的这个块实例序列,被称为构件C
的“重新激活链”。除了X
之外,这个链上的所有构件头领,标定了生效而不运行的构件。在构件C
成为运行的时候,在它的重新激活链上所有块也成为运行的。
除了系统构件,程序执行还可以包含不属于特定系统的“独立对象构件”。任何这种构件的头领,都是一个脱离状态的对象,它局部于一个类对象或一个过程主体实例,也就是说不局部于某个系统头领。根据定义,独立构件总是不生效的,只能对它执行call
过程。
协程和生成器
准并行系统的detach
/resume
机制,是一种协程[30]。在Simula 67最初标准中,没有恢复状态,并且没有规定call
过程,但call
过程通常会出现在当时的语言实现中[31]。detach
/call
机制,相当于现在称为生成器的“半协程”,而最初标准中的resume
过程可理解为detach
过程与call
过程的组合[32]。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!示例:
begin
outtext("Hello, World!");
outimage;
end;
outtext
过程将字符串输出到缓冲区,而outimage
过程将缓冲区内容输出到标准文件,二者都定义在输出文件类OutFile
中,而它是文件类File
的子类。
二叉树
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标准通过如下实例来诠释叫做“准并行系统”的协程机制:
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) ← PSC
这里方括号表示系统头领,圆括号表示其他种类的块实例,左向箭头表示系附,使用粗体表示这个块实例执行了对象生成式。执行第6行的detach
语句后,对象构件头领X1
进入脱离状态,保存X1
的重新激活点为第6行P1
过程体结束,回溯到第28行对象产生式结束,系统S1
的对象X1
生成完毕,这时的情况是:
[S1] ← PSC
|
(X1) ← (P1) ← <X1重新激活点>
这里的竖杠将对象构件头领列为一个系统的成员。PSC到达系统S1
主构件第29行,开始生成对象X2
,进入类C2
,PSC到达第17行的类主体中匿名块开始处,进入这个子块产生系统S2
的系统头领,PSC到达第24行的系统S2
主构件的第一个可执行语句,开始生成对象X3
,进入类C3
,PSC到达第20行类主体开始处,这时的情况是:
[S1] ← (X2) ← [S2] ← (X3) ← PSC
|
(X1) ← (P1) ← <X1重新激活点>
执行第21行类主体的第一个可执行语句detach
后,对象构件头领X3
进入脱离状态,保存X3
的重新激活点为第22行,回溯到第24行对象产生式结束,系统S2
的对象X3
生成完毕,这时的情况是:
[S1] ← (X2) ← [S2] ← PSC
| |
| (X3) ← <X3重新激活点>
|
(X1) ← (P1) ← <X1重新激活点>
PSC到达系统S2
主构件的第25行,执行resume(X3)
语句后,保存系统S2
主构件的重新激活点为第26行,对象构件头领X3
进入恢复状态,PSC恢复到第22行的对象X3
的类主体之中,这时的情况是:
[S1] ← (X2) ← [S2] ← <S2重新激活点>
| |
| (X3) ← PSC
|
(X1) ← (P1) ← <X1重新激活点>
这里的下划线指示这个块实例处在恢复状态。执行第22行的类C2
的特性过程P2
,到达在第14行的过程体的第一个可执行语句,这时的情况如下,并标记为“状况A”:
! 状况A ; [S1] ← (X2) ← [S2] ← <S2重新激活点> | | | (X3) ← (P2) ← PSC | (X1) ← (P1) ← <X1重新激活点>
执行在第14行P2
过程体的detach
语句后,对象构件头领X2
进入脱离状态,保存X2
的重新激活点为第15行,回溯到第29行对象产生式结束,系统S1
的对象X2
生成完毕,这时的情况如下,并标记为“状况B”:
!状况B ; [S1] ← PSC | (X1) ← (P1) ← <X1重新激活点> | (X2) ← [S2] ← <S2重新激活点> | (X3) ← (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重新激活点>
|
(X2) ← [S2] ← <S2重新激活点>
|
(X3) ← (P2) ← PSC
序列(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重新激活点>
|
(X1) ← (P1) ← PSC
|
(X2) ← [S2] ← <S2重新激活点>
|
(X3) ← (P2) ← <X2重新激活点>
抽象类
下面例子中,定义了一个字形类Glyph
,它是抽象基础类,并且有二个实现子类:字符类Char
和行类Line
:
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分钟,每个行动都服从常态分布,他们的试衣间经历被模拟如下:
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
的有前缀块。模拟器类可在任何块上使用,而且模拟器甚至可以嵌套,比如在模拟某人做模拟的时候[33]。时间过程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分钟的模拟时间。
参见
注释
- ^ https://portablesimula.github.io/github.io/.
- ^ 2.0 2.1 Portable Simula Revisited. [2022-03-15]. (原始内容存档于2022-05-11).
- ^ 3.0 3.1 GNU Cim. [2022-04-02]. (原始内容存档于2022-04-14).
- ^ Kristen Nygaard; Ole-Johan Dahl. The Development of the Simula Languages (PDF). 1978 [2022-03-14]. (原始内容 (PDF)存档于2022-01-20).
SIMSCRIPT was the only simulation language that we were closely acquainted with during the design phase of SIMULA. From the preceding sections it will be evident that it had a considerable impact through its list processing and time scheduling mechanisms. It also contained a set of random drawing and other utility routines, which served as a model for our procedure library.
- ^ 5.0 5.1 5.2 Alan Kay. The Early History of Smalltalk. [2022-04-11]. (原始内容存档于2011-04-29).
I wound up in graduate school at the University of Utah in the Fall of 1966, ……. …… The documentation was incomprehensible. Supposedly, this was the Case-Western Reserve 1107 Algol – but it had been doctored to make a language called Simula; the documentation read like Norwegian transliterated into English, which in fact it was. There were uses of words like activity and process that didn’t seem to coincide with normal English usage. ……
The weirdest part was the storage allocator, which did not obey a stack discipline as was usual for Algol. …… What Simula was allocating were structures very much like the instances of Sketchpad. There wee descriptions that acted like masters and they could create instances, each of which was an independent entity. What Sketchpad called masters and instances, Simula called activities and processes. Moreover, Simula was a procedural language for controlling Sketchpad-like objects, thus having considerably more flexibility than constraints (though at some cost in elegance) ……. ……
For the first time I thought of the whole as the entire computer and wondered why anyone would want to divide it up into weaker things called data structures and procedures. Why not divide it up into little computers, as time sharing was starting to? But not in dozens. Why not thousands of them, each simulating a useful structure? ……
It is not too much of an exaggeration to say that most of my ideas from then on took their roots from Simula – but not as an attempt to improve it. It was the promise of an entirely new way to structure computations that took my fancy. As it turned out, it would take quite a few years to understand how to use the insights and to devise efficient mechanisms to execute them. - ^ 6.0 6.1 Barbara Liskov. A history of CLU (PDF). 1992 [2022-04-27]. (原始内容 (PDF)存档于2021-11-05).
Programming languages that existed when the concept of data abstraction arose did not support abstract data types, but some languages contained constructs that were precursors of this notion. …… The mechanism that matched the best was the class mechanism of Simula 67. A Simula class groups a set of procedures with some variables. A class can be instantiated to provide an object containing its own copies of the variables; the class contains code that initializes these variables at instantiation time. However, Simula classes did not enforce encapsulation ……, and Simula was lacking several other features needed to support data abstraction, …….
- ^ 7.0 7.1 7.2 SIMULA Standards Group. SIMULA Standard (PDF). 1986 [2022-03-23]. (原始内容 (PDF)存档于2022-04-07).
In this Standard the name SIMULA is considered synonymous with SIMULA 67. …… It is recommended that the language defined in this Standard be referred to as "Standard SIMULA".
SIMULA includes most of the ALGOL 60 language. Wherever ALGOL is used in this Standard it relates to the STANDARD ALGOL 60 definition (ISO 1538). - ^ 8.0 8.1 Ole-Johan Dahl. The Birth of Object Orientation: the Simula Languages (PDF). 2001 [2022-04-13]. (原始内容 (PDF)存档于2021-08-10).
On the other hand, if a procedure
P
is specified as virtual in a classC
the binding scheme is semi-dynamic. Any call forP
occurring inC
or in any subclass ofC
will bind to that declaration ofP
which occurs at the innermost prefix level of the actual object containing such a declaration (and similarly for remote accesses). Thus, the body of the procedureP
may, at the prefix level ofC
, be postponed to occur in any subclass ofC
. It may even be replaced by more appropriate ones in further subclasses.
This binding scheme is dynamic in the sense that it depends on the class membership of the actual object. But there is nevertheless a degree of compiler control; the access can be implemented as indirect through a table produced by the compiler forC
and for each of its subclasses. ……
a virtual procedure can be seen as a parameter, where the actual parameter is a procedure residing safely within the object itself, at an appropriate prefix level. There is the additional advantage that the procedure has direct access to attributes of the object containing it. - ^ 9.0 9.1 O. -J. Dahl; C. A. R. Hoare. Hierarchical Program Structures. C. A. R. Hoare (编). Structured Programming. London, UK: Academic Press. 1972: 175–220. ISBN 978-0122005503.
In SIMULA 67, a block instance is permitted to outlive its calling statement, and to remain in existence for as long as the program needs to refer to it. It may even outlive the block instance that called it into existence. As a consequence, it is no longer possible to administer storage allocation as a simple stack; a general garbage-collector, including a scan-mark operation, is required to detect and reclaim those areas of store (local workspace of block instances) which can no longer be referenced by the running program. The reason for accepting this extra complexity is that it permits a wider range of concepts to be conveniently expressed. In particular, it clarifies the relationship between data and the operations which may be performed upon it, in a way which is awkward or impossible in ALGOL 60.
- ^ 10.0 10.1 Ole-Johan Dahl. The Birth of Object Orientation: the Simula Languages (PDF). 2001 [2022-04-13]. (原始内容 (PDF)存档于2021-08-10).
The main impact of Simula 67 has turned out to be the very wide acceptance of many of its basic concepts: objects, but usually without own actions, classes, inheritance, and virtuals, often the default or only way of binding “methods”, (as well as pointers and dynamic object generation).
There is universal use of the term “object orientation”, OO. Although no standard definition exists, some or all of the above ideas enter into the OO paradigm of system development. There is a large flora of OO languages around for programming and system specification. …… The importance of the OO paradigm today is such that one must assume something similar would have come about also without the Simula effort. The fact remains, however, that the OO principle was introduced in the mid 60’s through these languages.
Simula 67 had an immediate success as a simulation language, and was, for instance extensively used in the design of VLSI chips, e.g. at INTEL. As a general programming language, its impact was enhanced by lectures at NATO Summer Schools given by OJD, materialized as a chapter in a book on structured programming. The latter has influenced research on the use of abstract data types, e.g., the CLU language, as well as research on monitors and operating system design.
A major new impact area opened with the introduction of workstations and personal computers. Alan Kay and his team at Xerox PARC developed Smalltalk, an interactive language building upon Simula’s objects, classes and inheritance. It is oriented towards organising the cooperation between a user and her/his personal computer. - ^ Kristen Nygaard. SIMULA: An Extension of ALGOL to the Description of Discrete-Event Networks. Proceedings of the IFIP congress 62, Munich. North-Holland Publ., pages 520-522. Aug 1962.
- ^ C. A. R. Hoare. Record Handling (PDF). ALGOL Bulletin no. 21. 1965 [2022-03-14]. (原始内容 (PDF)存档于2022-04-07).
- ^
Jan Rune Holmevik. Compiling Simula. Oslo, Norway: Institute for Studies in Research and Higher Education. 1994. (原始内容存档于2020-01-11).
During the summer and autumn of 1963, …… Instead Dahl and Nygaard introduced the far more powerful process concept which came to constitute the basic, unifying feature of the SIMULA I language. In short, a process can be understood as a generalized ALGOL procedure with quasi-parallel properties. ……
they became more and more preoccupied with the opportunities embedded in Tony Hoare's record class construct, first presented in ALGOL bulletin no. 21, 1965. …… What they were really looking for was some kind of generalized process concept with record class properties. The answer to their problem suddenly appeared in December 1966, when the idea of prefixing was introduced. A process, later called an object, could now be regarded as consisting of two layers: A prefix layer containing references to its predecessor and successor along with a number of other properties, and a main layer containing the attributes of the object in question. In addition to this important new feature, they also introduced the class concept, which can roughly be described as a highly refined version of SIMULA I's activity concept. This powerful new concept made it possible to establish class and subclass hierarchies of concatenated objects. - ^ Ole-Johan Dahl; Kristen Nygaard. Class and Subclass Declarations (PDF). North Holland: J. Buxton,ed.: Simulation Programming Languages. Proceedings from the IFIP Working Conference in Oslo, May 1967. 1968 [2020-05-16]. (原始内容 (PDF)存档于2022-04-08).
- ^ Ole-Johan Dahl; Bjørn Myhrhaug; Kristen Nygaard. SIMULA 67 Common Base Language (PDF). Norwegian Computing Center. 1968, 1970 [2022-02-13]. (原始内容 (PDF)存档于2022-04-07).
- ^ Swedish standard SS 63 61 14 (PDF). [2022-04-14]. (原始内容 (PDF)存档于2022-04-16).
- ^ Bjarne Stroustrup. A history of C++: 1979-1991 (PDF). History of programming languages---II (ACM). 1996: 699–769 [2022-03-22]. doi:10.1145/234286.1057836. (原始内容 (PDF)存档于2022-04-23).
C++ was designed to provide Simula’s facilities for program organization together with C’s efficiency and flexibility for systems programming.
James Gosling. The Feel of Java (PDF). 1997 [2022-04-27]. (原始内容 (PDF)存档于2022-02-28).Java is a blue collar language. It’s not PhD thesis material but a language for a job. Java feels very familiar to many different programmers because I had a very strong tendency to prefer things that had been used a lot over things that just sounded like a good idea. …… It has an object-oriented flavor that derives from a number of languages—Simula, C/C++, Objective C, Cedar/Mesa, Modula, and Smalltalk.
- ^ O. -J. Dahl; C. A. R. Hoare. Hierarchical Program Structures. C. A. R. Hoare (编). Structured Programming. London, UK: Academic Press. 1972: 175–220. ISBN 978-0122005503.
A procedure which is capable of giving rise to block instances which survive its call will be known as a class; and the instances will be known as objects of that class. A class may be declared, with or without parameters, in exactly the same way as a procedure ……
- ^ O. -J. Dahl; C. A. R. Hoare. Hierarchical Program Structures. C. A. R. Hoare (编). Structured Programming. London, UK: Academic Press. 1972: 175–220. ISBN 978-0122005503.
useful properties from the standpoint of concept modelling. ……
⑷ Language element. A block is itself a statement, which is a syntactic category of the language. Furthermore, through the procedure mechanism, reference to a block may be dissociated from its defining text. - ^ O. -J. Dahl; C. A. R. Hoare. Hierarchical Program Structures. C. A. R. Hoare (编). Structured Programming. London, UK: Academic Press. 1972: 175–220. ISBN 978-0122005503.
One of the most powerful mechanisms for program structuring in ALGOL 60 is the block and procedure concept. It has the following useful properties from the standpoint of concept modelling.
⑴ Duality. A block head and block tail together define an entity which has properties and performs actions. Furthermore the properties may include a data structure as well as associated operators (local procedures). - ^ O. -J. Dahl; C. A. R. Hoare. Hierarchical Program Structures. C. A. R. Hoare (编). Structured Programming. London, UK: Academic Press. 1972: 175–220. ISBN 978-0122005503.
In ALGOL 60, the rules of the language have been carefully designed to ensure that the lifetimes of block instances are nested, in the sense that those instances that are latest activated are the first to go out of existence. It is this feature that permits an ALGOL 60 implementation to take advantage of a stack as a method of dynamic storage allocation and relinquishment. But it has the disadvantage that a program which creates a new block instance can never interact with it as an object which exists and has attributes, since it has disappeared by the time the calling program regains control. Thus the calling program can observe only the results of the actions of the procedures it calls. Consequently, the operational aspects of a block are overemphasised; and algorithms (for example, matrix multiplication) are the only concepts that can be modelled.
- ^ 22.0 22.1 O. -J. Dahl; C. A. R. Hoare. Hierarchical Program Structures. C. A. R. Hoare (编). Structured Programming. London, UK: Academic Press. 1972: 175–220. ISBN 978-0122005503.
useful properties from the standpoint of concept modelling. ……
⑶ Class of instances. In ALGOL 60 a sharp distinction is made between a block, which is a piece of program text, and a dynamic block instance, which is (a component of) a computing process. An immediate and useful consequence is that a block may be identified with the class of its potential activations. (Strictly speaking a "block" in this context means either the outermost block or a block immediately enclosed by a dynamic block instance.) Through the recursion mechanism of ALGOL 60 different instances of the same block may co-exist in a computing process at the same time. - ^ O. -J. Dahl; C. A. R. Hoare. Hierarchical Program Structures. C. A. R. Hoare (编). Structured Programming. London, UK: Academic Press. 1972: 175–220. ISBN 978-0122005503.
useful properties from the standpoint of concept modelling. ……
⑵ Decomposition. A block where only local quantities are referenced is a completely selfcontained program component, which will function as specified in any context. Through a procedure heading a block (procedure) instance may interact with a calling sequence. Procedures which reference or change non-local variables represent a partial decomposition of the total task, which is useful for direct interaction with the program environment. - ^ D. A. Turner. Some History of Functional Programming Languages (PDF). in an invited lecture TFP12, St Andrews University. 12 June 2012 [2022-05-04]. (原始内容 (PDF)存档于2020-04-15).
Algol 60 allowed textually nested procedures and passing procedures as parameters (but not returning procedures as results). The requirement in the copying rule for systematic change of identifiers has the effect of enforcing static (that is lexicographic) binding of free variables.
In their book “Algol 60 Implementation”, Randell and Russell (1964, Sect. 2.2) handle this by two sets of links between stack frames. The dynamic chain links each stack frame, representing a procedure call, to the frame that called it. The static chain links each stack frame to that of the textually containing procedure, which might be much further away. Free variables are accessed via the static chain.
This mechanism works well for Algol 60 but in a language in which functions can be returned as results, a free variable might be held onto after the function call in which it was created has returned, and will no longer be present on the stack.
Landin (1964) solved this in his SECD machine. A function is represented by a closure, consisting of code for the function plus the environment for its free variables. The environment is a linked list of name-value pairs. Closures live in the heap. - ^ 25.0 25.1 Ole-Johan Dahl; Bjørn Myhrhaug. The Simula Implementation Guide (PDF). 1967, 1973 [2022-04-17]. (原始内容 (PDF)存档于2022-04-26).
A procedure deviates from a block in that ⑴ it has a name and ⑵ may be referred to at several different places in the program, and ⑶ that parameters may be given to it when invoked. A procedure shares the property of a block that it is impossible to establish a reference to it or to its interior.
- ^ D. A. Turner. Some History of Functional Programming Languages (PDF). in an invited lecture TFP12, St Andrews University. 12 June 2012 [2022-05-04]. (原始内容 (PDF)存档于2020-04-15).
Algol 60 is not normally thought of as a functional language but its rules for procedures (the Algol equivalent of functions) and variable binding were closely related to those of λ-calculus.
The Revised Report on Algol 60 (Naur 1963) is a model of precise technical writing. It defines the effect of a procedure call by a copying rule with a requirement for systematic change of identifiers where needed to avoid variable capture — exactly like β-reduction.
Although formal parameters could be declared value the default parameter passing mode was call by name, which required the actual parameter to be copied unevaluated into the procedure body at every occurrence of the formal parameter. This amounts to normal order reduction (but not graph reduction, there is no sharing). The use of call by name allowed an ingenious programming technique: Jensen’s Device. - ^ 27.0 27.1 Kristen Nygaard; Ole-Johan Dahl. The Development of the Simula Languages (PDF). 1978 [2022-03-14]. (原始内容 (PDF)存档于2022-01-20).
the ALGOL-like call by name parameters were out of the question for reasons of security and storage allocation strategy: the actual parameter could be lost during the lifetime of an object. The problem then was to find a name-parameter-like mechanism that would guarantee a safe place for the actual parameter. After much trial and error we hit on the virtual quantity concept where the actual would have to be declared in the object itself, but at a deeper subclass level than that of the virtual specification. Now generalized objects could be defined whose behaviour pattern could be left partly unspecified in a prefix class body. Different subclasses could contain different actual procedure declarations.
- ^ Ole-Johan Dahl. The Birth of Object Orientation: the Simula Languages (PDF). 2001 [2022-04-13]. (原始内容 (PDF)存档于2021-08-10).
In general attribute identifiers may be redeclared in subclasses, as is the case of inner blocks. The identity of an attribute is determined by the prefix level of the accessing occurrence, or, if the access is remote, by the class qualifying the object reference in question. In this way any ambiguity of identifier binding is resolved textually, i.e at compile time; we call it static binding.
- ^ Kristen Nygaard; Ole-Johan Dahl. The Development of the Simula Languages (PDF). 1978 [2022-03-14]. (原始内容 (PDF)存档于2022-01-20).
In ALGOL, blocks (including procedures) are seen externally as generalized operations. By introducing mechanisms for quasi-parallel sequencing, essentially the same construct could play the role of processes in parallel, and through mechanisms for naming block instances and accessing their contents they could function as generalized data objects. The essential benefits of combining data and operations in a single construct were already there to be explored.
- ^ Kristen Nygaard; Ole-Johan Dahl. The Development of the Simula Languages (PDF). 1978 [2022-03-14]. (原始内容 (PDF)存档于2022-01-20).
An object would start its life like an instance of a function procedure, invoked by the evaluation of a generating expression. During this phase the object might initialize its own local variables. Then, on passage through the end of the object or as the result of a new basic operation "detach", control would return to the generating expression delivering a reference to the object as the function value. In the former case the object was "terminated" with no further own actions, in the latter case it had become a "detached object" capable of functioning as a "coroutine".
The basic coroutine call "resume (<object reference>)" would make control leave the active object, leaving behind a reactivation point at the end of the resume statement, and enter the referenced object at its reactivation point. - ^ Ole-Johan Dahl; Bjørn Myhrhaug. The Simula Implementation Guide (PDF). 1967, 1973 [2022-04-16]. (原始内容 (PDF)存档于2022-04-16).
The procedure "call" is not a part of the Common Base, but is a natural part of a SIMULA 67 Common Base implementation. ……This definition of call is tentative, since the problem is currently being studied by a Technical Committee under the SIMULA Standards Group.
- ^ O. -J. Dahl; C. A. R. Hoare. Hierarchical Program Structures. C. A. R. Hoare (编). Structured Programming. London, UK: Academic Press. 1972: 175–220. ISBN 978-0122005503.
In SIMULA, a coroutine is represented by an object of some class, co-operating by means of resume instructions with objects of the same or another class, which are named by means of reference variables. ……
Thus a main program may establish a coroutine relationship with an object that it has generated, using the call/detach mechanism instead of the more symmetric resume/resume mechanism. In this case, the generated object remains subordinate to the main program, and for this reason is sometimes known as a Semicoroutine. ……
LetX
andY
be objects, generated by a "master program"M
. Assume thatM
issues acall (X)
, thereby invoking an "active phase" ofX
, terminated by a detach operation inX
; and then issues acall (Y)
, and so forth. In this wayM
may act as a "supervisor" sequencing a pattern of active phases ofX
,Y
, and other objects. Each object is a "slave", which responds with an active phase each time it is called for, whereas M has the responsibility to define the large scale pattern of the entire computation.
Alternatively the decision making may be "decentralised", allowing an object itself to determine its dynamic successor by a resume operation.
The operationresume (Y)
, executed byX
, combines an exit out ofX
(bydetach
) and a subsequentcall (Y)
, thereby bypassingM
. Obligation to return toM
is transferred toY
. - ^ Jaroslav Sklenar. INTRODUCTION TO OOP IN SIMULA. 1997 [2022-02-13]. (原始内容存档于2022-05-11).
The system class Simulation introduces the notion of time. It means that if nested, there will be other local(nested) times.
延伸阅读
- Sylvester, Peter. IBM System 360/370 Compiler and Historical Documentation (The Simula Standard and other historical documentation). [2020-05-15]. (原始内容存档于2019-10-01).
- Pooley, Rob, An Introduction to Programming in Simula (PDF), Alfred Waller Ltd, 1987 [2022-03-15], ISBN 0632016116, (原始内容 (PDF)存档于2022-04-07)
外部链接
|
|