Modula-3

Modula-3是一种系统编程语言，它是叫做的升级版本的Modula-2的后继者。虽然它已经在研究界有了影响力，受其影响的语言有Java、C#和Python，但未能在工业上被广泛采用。它是在1980年代末由在数字设备公司（DEC）（SRC）和Olivetti研究中心（ORC）的、James Donahue、Lucille Glassman、Mick Jordan（之前在Olivetti软件技术实验室工作）、Bill Kalsow和设计。 
Modula-3的主要特点，是保持系统编程语言的强力，同时具有简单性和安全性。Modula-3意图延续Pascal的类型安全和Modula-2的模块化编程传统，同时为实际编程引入新构造。特别是Modula-3添加了如下支持：例外处理、关键字参数与缺省参数值、有跟踪的引用与垃圾回收、面向对象、不透明类型及其披露、泛型、多线程和不安全代码显式标记。Modula-3的设计目标，是以非常基本的形式，实现指令式编程语言的大部份重要的现代特征。因此省略了涉嫌危险和复杂的特征，如多重继承和运算符重载。
发展历史.
Modula-3项目始于1986年11月，当时莫里斯·威尔克斯向尼克劳斯·维尔特写信，提出了一些关于Modula新版本的想法。威尔克斯在此之前一直在DEC工作，他回到英格兰并加入了Olivetti的研究策略委员会。Wirth已经转移到了Oberon，但Wilkes的团队在Modula名下继续开发没有任何问题。语言定义于1988年8月完成，并于1989年11月更新了版本。DEC和Olivetti的编译器很快就出现了，随后又有了第三方实现。
它的设计受到和Acorn计算机研究中心（ARC，在Olivetti收购Acorn之后叫做ORC）在语言上的工作的很大影响，是编写多处理器VAX工作站的操作系统的语言，还是在基于ARM的系列计算机的项目中，ARC编写用于Acorn C和模块执行库（CAMEL）的Acorn编译器的语言。正如修订的Modula-3报告所述，该语言演化自Mesa、Modula-2、Cedar和，还受到其他语言的影响如：Object Pascal，Oberon和。
在20世纪90年代，Modula-3作为一种教学语言获得了可观的传播，华盛顿大学在1994年发行的微内核操作系统是用Modula-3开发的，但它从未广泛用于工业用途。造成这种情况的原因，可能是Modula-3的关键支持者DEC的消亡，特别是在1998年DEC被出售给康柏之前就不再有效地维护它了。无论如何，尽管Modula-3有着简单性和强大功能，似乎当时对有限实现了面向对象编程的过程式编译语言的需求很少。
Critical Mass公司曾在一段时间内提供了一个名为CM3的商业编译器，它由以前DEC SRC的在DEC被出售给Compaq之前雇用的一个主要实现者维护，和叫做Reactor的一个集成开发环境以及可扩展的Java虚拟机（以二进制和源格式许可发行并可用Reactor来构建）。但该公司在2000年停止了活动，并将其产品的一些源代码提供给了elego软件解决方案有限公司。
基本上，Modula-3的唯一企业支持者是elego软件解决方案有限公司，它继承了Critical Mass的资源，并且已经以源代码和二进制形式发布了几个版本的CM3系统。Reactor IDE在几年后才开源发布，新的名称是CM3-IDE。2002年3月，elego还接管了此前由蒙特利尔工程学院维护的另一个活跃的Modula-3发行版PM3的存储库，在此后几年间以HM3名义进行了持续改进，直至最终被淘汰。
Modula-3是文档记录了语言特征演化的少见语言之一，在《用Modula-3系统编程》中，收录了设计者对四个设计要点的讨论：结构等价与名称等价、子类型规则、泛型模块和参数模态。Modula-3现在主要是在大学中的比较编程语言课程中教授，其教科书已绝版。
程序示例.
Modula-3中所有的程序至少具有一个模块文件，而大多数程序还包括一个接口文件，客户端使用它来访问来自模块的数据。语言语法的一个常见示例是Hello world程序：
MODULE Main; 
 IMPORT IO;
BEGIN
 IO.Put("Hello World\n")
END Main.
与其他语言一样，Modula-3程序必须有导出codice_1接口的实现模块，如上述示例中实现模块缺省导出了同名的接口，它可以处在名为Main.m3的文件中；或者是通过codice_2导出codice_1接口的实现模块，比如：
MODULE HelloWorld EXPORTS Main; 
 IMPORT IO;
BEGIN
 IO.Put("Hello World\n")
END HelloWorld.
建议这个模块所在的文件的名字与实际模块名字相同，当它们不同时编译器只是发出一个警告。
语言设计理念.
Modula-3委员会将Modula-3设计为ALGOL语言家族的现代代表，他们认为：
ALGOL家族语言都有强类型系统，其基本想法是把值空间划分成类型，限制变量持有单一类型的值，并且限制运算应用于固定类型的运算数。这个语言家族的趋势，在1960年代是朝向控制流程和数据结构特征，在1970年代是朝向特征如接口、不透明类型和泛型，在1980年代是采纳来自LISP家族和始自BCPL的C家族的仔细挑选的技术。
Modula-3委员会坚持一个自称为“复杂度预算”的设计原则：在五十页内将语言准确而易读的描述出来，为此只容纳最有用的特征。精选出来的特征直接针对两个主要目标：
这些特征中任何一个都没有特殊的新颖性，但是组合起来是简单而强力的。人们越是更好的理解程序，就越是使用更大的建造块去构造它。指令之后是语句，语句之后是过程，过程之后是接口，下一步应当是抽象类型。在理论层面，抽象类型，是通过其操作的规定，而非通过其数据的表示，来定义的类型。
词法记号.
关键字：
保留标识符：
运算符：
此外，注释是开于codice_4并合于codice_5的任意字符的序列。注释可以嵌套并可以扩展跨越多于一行。
基础性语法和语义.
Modula-3承袭了ALGOL家族传统的强类型，并且为了使类型系统尽可能的统一，采用了如下原则：
一个典型的不安全运行时间错误，是释放仍可由活跃引用（悬空指针）触及的数据结构。避免这个问题的唯一可靠方式，是不再触及存储的自动释放，或称为垃圾收集。Modula-3因而提供了有跟踪的引用，它类似于Modula-2的指针，除了它们所指向的存储被保存在有跟踪的堆之中之外，在这里它们将在到它们的引用都消失时自动释放。垃圾收集的另一个巨大利益是简化了接口，没有垃圾收集，接口就必须规定客户或实现是否有责任释放每个分配的引用，和在什么条件下可以安全的去这么做。
例外是一次退出多层作用域的控制结构。一个例外被重复的发起退出当前活跃的作用域，直到找到给这个例外的处理器（handler），并将控制权移交给这个处理器，如果没有处理器，则计算以某种依赖于系统的方式终止，比如进入调试器。在Modula-3中，codice_11和codice_12语句的语义，分别使用退出例外codice_13和返回例外codice_14来定义。Modula-3的设计者认为语言实现，除了这种返回例外和退出例外之外，更应当以各种例外为代价，加速正常的结果产出；为每个引发的例外花费一千条指令，来为每个过程节约一条指令都是合理的。 
块与声明.
声明介入一个常量、类型、变量、例外或过程的一个名字。这个名字的作用域是包含这个声明的块。一个块可以出现为一个模块或过程的本体，或一个块语句，Pascal和Modula-2没有块语句。一个块有如下形式：
 Decls
BEGIN
 S
END
这里的codice_15是成序列的声明，而codice_16是一个语句，经常是顺序复合语句即成序列的语句，它是这个块的执行部份。这里的codice_15与codice_16在缩进上对齐，是因为在Pascal语言家族中，可能含有嵌套过程声明的声明序列的位置相较于ALGOL 60，从紧随在关键字codice_19之后转移到了紧靠在其前面。一个块的声明可以介入一个名字最多一次，然而一个名字可以在嵌套的块中重新声明。在块中这些声明的次序，除了确定变量的初始化次序之外无关紧要。
一个Modula-3程序规定了一个计算，它作用在一序列的叫做地点（location）的数字元件之上。变量是地点的一个集合，它依据这个变量的类型所确定的约定，表示一个数学上的。如果一个值可以由类型codice_7的某个变量来表示，则称谓这个值是codice_7的一个成员，并且codice_7包含这个值。
声明为一个变量、类型、过程、或例外指定一个被称为标识符的作为名字。一个声明应用于其上的程序区域，叫做这个声明的作用域。作用域是可以嵌套的。一个标识符的含义，由这个标识符在其中声明的最小的包围作用域来确定。
类型声明有如下形式：，这里的codice_7是一个标识符，而codice_8是一个类型或类型表达式。在需要类型的各种地方，通常允许类型表达式。
变量声明有如下形式：，这里的codice_25是一个标识符，codice_7是除了开放数组之外的一个非空类型，而codice_27是一个表达式。它声明了codice_25是类型codice_7的一个变量，其初始值是codice_27的值。codice_31和codice_32任何一个都可省略，但二者不能都省略。如果省略了codice_7，它采用codice_27的类型。如果省略了codice_27，初始值是类型codice_7的任意值。如果二者都存在，codice_27必须可赋值给codice_7。初始值是一种简写，等价于在随后的块的可执行部份开始处插入赋值codice_39，如果多个变量有初始值，按它们声明的次序插入它们的赋值。形式，是的简写。
声明有如下形式：，这里的codice_25是一个标识符，codice_7是一个类型，而codice_42是一个常量表达式。它声明codice_25为具有类型codice_7和值codice_42的一个常量。codice_32可以省略，在这种情况下codice_25的类型是codice_42的类型。如果codice_7存在，则它必须包含codice_42。
值与类型.
Modula-3的类型系统使用，而非Modula-2的即类型构造子的每次出现产生一个新类型。在结构等价中，在如果两个类型的定义在展开的时候，也就是在所有常量表达式都被替换为它们的值，并且所有类型名字都被替代为它们的定义的时候，变成相同的，则这两个类型是相同的。在递归数据类型的情况下，这个展开是部份展开的无穷极限。
有三种序数类型：枚举、子范围和整数类型，预定义了如下序数类型：codice_51、codice_52、codice_53（如同子范围codice_54）、codice_55（枚举codice_56）和codice_57（包含至少256个元素表示ISO-Latin-1代码的枚举）。整数和枚举元素合起来叫做序数值。序数值codice_58如果是整数或扩展精度整数，则codice_58的基础类型分别是codice_51或codice_52，否则codice_58的基础类型是包含它的那个唯一性的枚举类型。两个序数如果有相同的值则二者相等。
有三种浮点类型：codice_63、codice_64和codice_65，这些类型的属性规定在对应的必要接口中。两个浮点数如果定义它们的底层实现相同则二者相等。
一个引用值要么是codice_66，要么是一个叫做所引用者（referent）的变量的地址。预定义了如下引用类型：codice_67，它包含所有的有跟踪的引用，codice_68，它包含所有的无跟踪的引用，和codice_69，它只包含codice_66。Modula-3支持在运行时间的数据分配。分配操作有如下形式：，这里的codice_7是除了预定义的codice_67、codice_68或codice_69之外的引用类型。两个引用如果寻址相同的地点则二者相等。
记录是叫做这个记录的字段的命名变量的一个序列，记录类型的声明有如下形式：，在这里的类型表达式中，codice_75是类型构造子，而它的实际参数是字段声明的一个列表codice_76，其中每个字段都有如下形式：，这里的字段名字codice_77是一个标识符，而字段类型codice_78是除了开放数组之外的非空类型，而字段缺省值codice_79是一个常量表达式。形式，是的简写。两个记录如果有相同的字段，并且对应的字段都相等则二者相等。
在Modula-3中的对象，非常类似于Simula 67中的对象：它们总是引用，它们拥有数据字段和方法二者，并且它们有着单一继承而非多重继承。预定义了如下对象类型：codice_80，它是没有字段或方法的有跟踪的对象类型，和codice_81，它是没有字段或方法的无跟踪的对象类型。
是一个名字，它指示某个给定引用类型的未知子类型。预定义了两个：codice_82和codice_83，它们分别表示文本串和互斥信号量，它们的属性分别规定于必要接口codice_84和codice_85之中。 
数组是叫做这个数组的元素的组成变量的有索引的搜集。有两种数组类型，固定的和开放的。固定数组的长度是在编译时间确定的，开放数组的长度是在运行时间分配确定的，这个长度此后不能改变。两个数组如果有相同的长度，并且对应的元素都相等则二者相等。
压缩类型的变量出现在记录、对象或数组中，占据指定数量的位元，并毗邻于前导的字段或元素紧压打包。压缩类型的声明有如下形式：，这里的codice_86是一个基础类型，而codice_87是整数取值的常量表达式。
集合是取自某个序数类型的值的搜集。集合类型声明有如下形式：，这里的基础类型codice_86是一个序数类型。两个集合如果有相同的元素则二者相等。
不包含值的一个类型是空的，例如codice_89是一个空类型。空类型可以用来建造非空类型，比如codice_90。声明一个空类型的一个变量是静态错误。
所有的都有一个唯一的类型，但是一个值可以是很多类型的一个成员，例如值codice_91是codice_92和codice_51二者的成员，因此论述“一个值的类型”将会是有歧义的。短语“codice_94的类型”，意味着“表达式codice_94的类型”；然而短语“codice_94是codice_7的成员”，意味着“codice_94的值是codice_7的成员”。但在有一种情况下，一个值可以称为有着一个类型：所有的对象或有跟踪的引用值，包括了一个类型代码，它叫做这个引用值的分配类型。分配类型通过codice_100语句来测试。
语言规定提供了类型运算codice_101、codice_102和codice_103，分别返回变量codice_94或者类型codice_7的那些变量的位元数目、8位字节的数目和可寻址地点的数目。在所有情况下，codice_94必须是指定式，而codice_7必须不是开放数组类型，指定式codice_94只在它的类型是开放数组的时候求值。
序数类型.
枚举类型声明有如下形式：，这里的codice_25是各不相同的标识符，类型codice_7是codice_87个值的有序集合，表达式codice_112指示这个类型在递增次序下的第codice_113个值，空枚举codice_114是允许的。
子范围类型声明有如下形式：，这里的codice_115和codice_116是有相同的基础类型的两个序数值，codice_7的值是从codice_115到codice_116含二者所有的值，它们必须是常量表达式，如果codice_115超过codice_116，则子范围是空的。
每个不同的枚举类型介入新的一组值，而子范围类型重复使用底层类型的值。例如：
TYPE
 T1 = {A, B, C};
 T2 = {A, B, C};
 U1 = [T1.A..T1.C];
 U2 = [T1.A..T2.C]; (* sic *) 
这里的codice_122和codice_123是相同类型，因为它们有相同的展开定义。codice_124和codice_125也是相同的类型，因为codice_126。类型codice_122和codice_124却是不同的，尽管它们包含相同的值，但是codice_122的展开定义是枚举，而codice_124的展开定义是子范围。类型codice_131是第三个类型，它的值codice_132和codice_133无关于值codice_134和codice_135。
语言规定提供了支持序数类型的一些类型运算：对于序数类型codice_7，codice_137返回在codice_7中元素的数目，codice_139返回codice_7的最小值，codice_141返回codice_7的最大值。codice_143将一个枚举的元素codice_144，转换成表示它在枚举次序中所处位置的整数，如果元素的类型是枚举codice_7的子范围，结果是元素在codice_7内的位置，而非在子范围内。codice_147将表示在枚举类型codice_7中位置的整数codice_113，转换成在这个枚举中此位置上的那个元素，如果codice_7是枚举类型的子范围，结果是在源出枚举类型的位置codice_113上的元素，而非在子范围内。如果codice_87是类型codice_7的一个整数，则有平凡的：codice_154。
数组类型.
固定数组类型声明有如下形式：，这里的索引类型codice_155是序数类型，而元素类型codice_156是除了开放数组之外的任何类型。开放数组类型声明有如下形式：，这里的元素类型codice_156是任何类型。
数组类型codice_7的值，是元素类型为codice_156的数组；对于固定数组类型，这个数组的长度是索引类型codice_155的元素数目；对于开放数组类型，这个数组的长度是任意的；开放数组的索引类型是codice_51子范围codice_162，这里的codice_87是这个数组的长度。开放数组类型只能用作形式参数的类型，引用类型的所引用者，另一个开放数组的元素类型，或作为数组构造子中的类型。
多维数组的形状是它每一维的长度的序列。更精确的说，一个数组的形状是它的长度跟随着任何它的元素的形状，非数组的形状是空序列。数组如果有相同的类型和形状，则它们是可赋值的。如果赋值的要么来源要么目标是开放数组，则需要运行时间检查。形如的表达式，是的简写。
如果codice_164有数组类型codice_7，则codice_166指示codice_164的一个指定元素，它在数组中的位置对应于codice_113在索引类型中位置。形如codice_169的表达式，是codice_170的简写。索引的解释由一个数组的类型确定，而非它的值；数组赋值改变数组变量的值，而非它的类型。例如：
VAR
 a := ARRAY [1..3] OF REAL {1.0, 2.0, 3.0};
 b: ARRAY [-1..1] OF REAL := a;
这里在codice_164或codice_172做元素值变更之前，codice_173为codice_174，尽管codice_175而codice_176。
可以使用codice_177，得到一个固定数组类型的索引类型中的元素数目，使用codice_178和codice_179，分别得到一个固定数组类型的索引类型的最小元素和最大元素。可以使用codice_180，得到一个数组的索引类型中的元素数目，使用codice_181和codice_182，分别得到一个数组的索引类型的最小元素和最大元素，在这种情况下，表达式codice_164只在它指示一个开放数组的时候求值。
引用类型.
一个引用类型要么是有跟踪的要么是无跟踪的。当到已分配的一段存储的所有的有跟踪的引用都消失时，系统对这段存储进行垃圾回收。有跟踪的引用类型以如下形式来声明：，这里的codice_78是任何类型。codice_7的值是到类型codice_78的变量的有跟踪的引用，codice_78叫做codice_7的所引用类型。无跟踪的引用类型以如下形式来声明：，这里的codice_78是任何无跟踪的类型（这个限制在不安全模块中解除）。
两个引用类型如果都是有跟踪的或都是无跟踪的，那么它们有相同的引用类。一个一般性的类型是有跟踪类型的条件为，它是有跟踪的引用类型，其任何字段类型是有跟踪类型的记录类型，其元素类型是有跟踪类型的数组类型，或其底层未压缩类型是有跟踪类型的压缩类型。
在有跟踪的和无跟踪的二者情况下，关键字codice_190可选的可以前导上codice_191，这里的codice_172是叫做铭牌（brand）的文本常量。铭牌用来区分原本上相同的类型，它们没有其他语义效果。在一个程序中所有铭牌都必须是唯一性的。如果codice_193出现而codice_172缺席，系统自动的为codice_172提供一个唯一的值。
分配操作返回codice_7的所引用类型的一个新分配的变量的地址，如果codice_7是对象类型，则返回到有配对的方法套件的一个新分配的数据记录的引用。codice_198返回的引用不同于所有的现存引用。这个新引用的分配类型是codice_7。如果codice_7的所引用类型为空则是一个静态错误。如果codice_7是到有codice_202个开放维度的数组的引用，codice_203操作有如下形式：，这里的这些codice_87是指定新数组在它的前codice_202个维度上的长度的整数取值表达式。
所有的对象类型和有跟踪的引用类型（包括codice_69）都有一个关联的整数代码。不同的类型有不同的代码。给一个类型的代码对程序的任何单一执行都是常量，但对不同的执行可以不同。类型运算codice_207，返回类型codice_7的这个代码，而codice_209，返回引用codice_210的分配类型的这个代码。如果codice_7是codice_67，或者不是对象类型或有跟踪的引用类型，则是一个静态错误。类型运算codice_213，测试codice_94是否为codice_7的成员，这里的codice_7必须是对象类型或有跟踪的引用类型，而codice_94必须可赋值给codice_7。
表达式.
规定产生一个值或变量的一个计算。所有的表达式都有一个静态确定的类型，它包含这个表达式可以产生的所有的值。在语法上，一个表达式要么是一个运算数，要么是应用于自身也是表达式的实际参数的一个运算。运算数是标识符、或类型。通过递归的求值一个运算的实际参数并应用这个运算，来求值一个表达式。实际参数的求值次序，对除了短路求值的codice_219和codice_220之外的所有运算都是未定义的。
对一个过程的调用，在这个过程是返回一个结果的函数过程的时候，是称为函数应用的一个表达式，这个表达式的类型是过程的结果类型。
Modula-3语言规定提供了前缀算术运算：codice_221、codice_222，中缀算术运算：codice_221、codice_222、codice_225、codice_226、codice_227、codice_228，数值函数运算：codice_229、codice_230、codice_231、codice_232、codice_233、codice_234、codice_235、codice_236，关系运算：codice_237、codice_238、 codice_239、codice_240、codice_241、codice_242，逻辑运算：codice_243、codice_244、codice_245，文本串接运算：codice_246，内存分配运算：codice_247，集合运算：并集codice_221、差集codice_222、交集codice_225、对称差集codice_226和隶属关系测试codice_252，它在序数codice_253是集合codice_254的元素的时候返回codice_174。
表达式可建构自如下：、实数文字、和。集合、数组和记录有构造子表达式：
表达式是一般性的表达式类的子集，所要求的限制使得可以静态的求值这个表达式。在常量表达式中，除了codice_198、解引用（显式的或隐式的）、codice_285、codice_286、codice_287、codice_288、codice_289和codice_290之外的所有运算都是合法的，唯独可运用的过程，是在提供无符号字运算的codice_291接口中的函数。变量在常量表达式中，只能作为给codice_292、codice_293、codice_294、codice_101、codice_102或codice_103的实际参数出现，并且这个变量必须不是开放数组类型的。文字和顶层过程常量在常量表达式中是合法的。
指定式.
产生变量的表达式叫做指定式（designator）。常量表达式永远不是指定式。指定式的类型是它产生的变量的类型。一个指定式依赖于上下文，可以指示要么一个变量，要么这个变量的值。比如，它作为赋值的左值时是变量，作为右值时是这个变量的值。一些指定式是只读的，它们不能用在可能改变这个变量的值的上下文中，并非只读的指定式就是可写的指定式。
一个标识符是可写的指定式的条件为，它被声明为变量，是codice_298或codice_299形式参数，codice_100或codice_301语句的局部变量，或绑定到可写指定式的codice_302局部变量。一个标识符是只读的指定式的条件为，它是codice_303形式参数，codice_304语句的局部变量，或绑定到非指定式或只读的指定式的codice_302局部变量。只有如下运算可以产生指定式：
子类型规则.
使用codice_375指示codice_7是codice_8的子类型，也就是codice_8是codice_7的超类型。子类型规则是：如果codice_375，则类型codice_7的所有值，都是类型codice_8的值，反过来则不成立。
对于序数类型codice_7和codice_8，codice_375的条件为，它们拥有相同的基础类型，并且所有的codice_7的成员都是codice_8的成员。就是说序数类型上的子类型，反映了在值集合上的子集关系。
对于数组类型，一个数组类型codice_266是一个数组类型codice_389的子类型的条件为，它们有相同的最终元素类型，相同的维度数，并且对于每个维度，要么二者都是开放的，要么codice_266是固定的而codice_389是开放的，要么它们都是固定的并有着相同的大小。
对于过程类型，codice_375的条件为，它们除了形式参数名字、缺省值和codice_393集合之外是相同的，并且codice_7的codice_393集合包含在codice_8的codice_393集合中。codice_66是所有的过程类型的一个成员。
对于压缩类型，codice_399和codice_7有相同的值，也就是说：codice_401并且codice_402。
对于集合类型，codice_403的条件是codice_404，就是说集合的子类型规则简单的使用值集合规则。
对于引用类型有：
NULL &lt;: REF T &lt;: REFANY
NULL &lt;: UNTRACED REF T &lt;: ADDRESS
就是说，codice_67和codice_68分别包含所有的有跟踪的和无跟踪的引用，codice_66是所有的引用类型的一个成员。这两个规则也适用于加铭牌的类型。
对于对象类型有：
ROOT &lt;: REFANY
UNTRACED ROOT &lt;: ADDRESS
NULL &lt;: T OBJECT ... END &lt;: T
就是说，所有对象都是引用，codice_66是所有的对象类型的一个成员，而所有的子类型都包括在它的超类型之中。第三个规则也适用于加铭牌的类型。
对于所有的codice_7，都有codice_410。对于所有的codice_411，codice_375并且codice_413，蕴涵codice_414。就是说codice_6是自反和传递的。但是codice_375并且codice_417，不蕴涵codice_7与codice_8是相同的，因为子类型关系不受形式参数名字、缺省值和压缩的影响。
可赋值性.
一个类型codice_7可赋值（assignable）给类型codice_8的条件是：
在第一种情况下，没有运行时间错误是有可能的。在第二种情况下，允许赋值一个codice_67到一个codice_429，还允许赋值一个codice_430到一个codice_431，这里分别要求对引用的类型和数组长度的运行时间检查；可以使用类型运算codice_432，将对象类型或有跟踪的引用类型的超类型变量当作其子类型的成员，这时需要运行时间检查，介入它就是为了以安全的方式，将codice_67变量赋值给确知的实际类型codice_429。在第三种情况下，常规的范围检查就能确保特定的codice_7的成员也是codice_8的成员。
一个表达式codice_253可赋值给一个变量codice_58的条件是：
一个表达式codice_253可赋值给类型codice_7，如果codice_253可赋值给类型codice_7的某个变量。如果codice_7不是一个开放数组，这等同声称codice_253可赋值给类型codice_7的任何变量。
语句.
执行一个语句产生一个计算，它可以正常产出结果（在可计算理论中称为停机），发起一个例外，导致一个必查的运行时间错误，或无限循环。如果结果是一个例外，它可以可选的配对上一个实际参数。如果一个表达式作为一个语句的执行部份而求值，这个求值引发一个例外，则这个例外成为这个语句的结果。空语句是无操作的（no-op），在语言报告中写为。
过程调用在这个过程是真正过程的时候是一个语句。调用一个函数过程并丢弃它的结果使用codice_451语句，它有如下形式：codice_452，这里的codice_253是一个表达式。它的效果是求值codice_253并忽略其结果。codice_12语句用于从过程中返回。
赋值语句有如下形式：，这里的codice_58是可写的指定式，而codice_253是可赋值给codice_58所指示变量的表达式。赋值语句将codice_58设置为codice_253的值，codice_58和codice_253的求值次序是未定义的，但codice_253会在codice_58更新之前求值。特别是，如果codice_58和codice_253是有交叠的子数组，以没有元素在被用作来源之前被用作目标的方式进行赋值。
顺序复合语句有如下形式：codice_467，它在Modula-2中称为“语句序列”。一些编程者使用分号作为语句终结符（terminator），另一些编程者将它用作分隔符（separator）；Modula-3允许这两种风格，语言报告将其用作分隔符。
块语句有如下形式：，这里的codice_15是一序列的声明，S是一个语句。块介入在codice_15中声明的常量、类型、变量和过程，并接着执行codice_16。声明的名字的作用域是这个块。
选择控制结构.
Modula-3提供了选择控制结构codice_471和codice_472语句。codice_471语句有如下形式：
IF B_1 THEN S_1
ELSIF B_2 THEN S_2
ELSIF B_n THEN S_n
ELSE S_0
END
这里的这些codice_389是布尔表达式，而这些codice_16是语句。codice_476和每个codice_477都是可选的。codice_471语句依次求值codice_389，直到某个codice_480求值为codice_174，则接着执行codice_482。如果没有表达式求值为codice_174，并且codice_476存在，则执行codice_485。如果没有表达式求值为codice_174，并且codice_476缺席，则codice_471语句是无操作的（no-op）。Modula-3的codice_472语句，不同于Modula-2的同名语句，它有如下形式：
CASE Expr OF 
 L_1 =&gt; S_1 
 | L_n =&gt; S_n
 ELSE S_0 
END
这里的codice_490是类型为序数类型的一个表达式，而每个codice_491是一个列表，这个列表构成自常量表达式，或用codice_492指示的常量表达式的范围，它表示从codice_493到codice_494含二者的值。如果codice_493超出了codice_494，这个范围为空。如果两个codice_491表示的集合有交叠，或者任何常量表达式不是类型codice_490的一个成员，则是一个静态错误。codice_476是可选的。
codice_472语句求值codice_490。如果结果的值在任何codice_502之中，则执行codice_482。如果这个值不在任何codice_502之中，并且codice_476存在，则执行它。如果这个值不在任何codice_502中，并且codice_476缺席，则发生一个必查的运行时间错误。一些编程者使用竖杠作为初始符（initiator），另一些编程者将它用作分隔符。Modula-3允许这两种风格，语言报告将其用作分隔符。
分配类型测试.
codice_100语句有如下形式：
TYPECASE Expr OF
 T_1 (v_1) =&gt; S_1
 | T_n (v_n) =&gt; S_n
 ELSE S_0
END
这里的codice_490是类型为引用类型的一个表达式，这些codice_16是语句，这些codice_7是引用类型，而这些codice_58是标识符。如果codice_490有类型codice_68，或者任何codice_7不是codice_490的类型的子类型，则为一个静态错误。codice_476和每个codice_518都是可选的。如果codice_519缺席，形式codice_520，是codice_521的简写。
codice_100语句求值codice_490，如果结果的引用值，是任何列出的类型codice_524的成员，则针对其中最小的codice_113，执行codice_482。codice_66是所有引用类型的一个成员，因此codice_69情况只有首先出现才能起作用。如果这个值不是列出的这些类型的一个成员，并且codice_476存在，则执行它。如果这个值不是列出的这些类型的一个成员，并且codice_476缺席，则发生一个必查的运行时间错误。每个codice_519声明类型为codice_524的一个变量，并且它的作用域是codice_482。如果codice_534存在，它在执行codice_482之前被初始化为codice_490的值。
可以使用codice_100语句测试类型为codice_67或对象的一个表达式的值的引用类型，比如写一个过程codice_539，在其中测试codice_210的值是codice_69、codice_542和codice_543中的哪一个类型。
循环控制结构.
Modula-3提供了循环控制结构codice_304、codice_545、codice_546和codice_547语句，和必须在字面上包围在这四个循环语句中，用于从循环中退出的codice_11语句。语句，循环直到codice_16中有一个发生。语句，被定义为：
LOOP
 IF B THEN S
 ELSE EXIT 
 END
END
语句，被定义为：
LOOP
 S; 
 IF B THEN EXIT END 
END
codice_304语句有如下形式：，这里的codice_25是一个标识符，而codice_552和codice_553是有相同基础类型的序数表达式，codice_554是整数取值的表达式。而codice_16是一个语句。codice_556是可选的，如果省略，codice_554缺省为codice_275。标识符codice_25指示其作用域是codice_16的一个只读变量，其类型是codice_552和codice_553共同的基础类型。codice_304语句的语义，可如下这样用codice_546和codice_302语句精确的定义为一个块：
 VAR
 i := ORD(first);
 done := ORD(last);
 delta := step;
BEGIN
 IF delta &gt;= 0 THEN
 WHILE i &lt;= done DO 
 WITH id = VAL(i, T) DO S END;
 INC(i, delta)
 END
 ELSE
 WHILE i &gt;= done DO
 WITH id = VAL(i, T) DO S END; 
 INC(i, delta)
 END
 END
END
这里的codice_7是codice_25的类型，codice_113、codice_569和codice_570代表不出现在codice_304语句中的变量。从codice_304语句的这个定义可以看出，如果一个codice_304循环的上界是codice_574或codice_575，就应当将它改写为codice_546循环来避免溢出。
With语句.
Modula-3的codice_302语句，无关于Modula-2的同名语句，它有如下形式：，这里的codice_25是一个标识符，codice_253是一个表达式，而codice_16是一个语句。codice_302语句声明了具有作用域codice_16的codice_25，作为变量codice_253的别名，或作为值codice_253的只读名字。表达式codice_253在进入codice_302语句的时候被求值一次。一个单一的codice_302语句可以包含多个绑定，它们顺序的求值，就是说，等价于。
codice_302语句可类比于某种过程调用codice_590，这里的codice_591被声明为：
PROCEDURE P(mode id: typeof e) =
BEGIN
 S
END P
这里的算符codice_592只是个示意，它不在语言规定之中；如果codice_253是可写的指定式，则codice_594是codice_298，否则codice_594是codice_303。在codice_302语句和调用codice_590之间唯一的区别为，出现在codice_302语句内的自由变量、codice_12和codice_11，是在codice_302语句所在的上下文中解释的，而非在新增加的codice_591的上下文中。
Modula-3语言报告使用codice_302语句定义其他语句，比如，codice_606和codice_607语句分别有如下形式：和，这里的codice_58指示一个序数类型的变量，而codice_87是可选的一个整数取值表达式，如果省略codice_87则缺省为codice_275，这两个语句分别等价于和，这里的codice_7是codice_58的类型，而codice_94代表不出现在codice_87中的变量。
例外.
例外声明有如下形式：codice_616，这里的codice_25是一个标识符，而codice_7是除了开放数组之外的一个类型，它声明了codice_25是具有实际参数类型codice_7的例外。如果省略了codice_621，这个例外不接受实际参数。例外声明只允许出现在接口和模块的最外层作用域之中。所有的声明的例外都是各不相同的。
codice_622语句有两种形式，没有实际参数的形式：，这里的codice_253是不接受实际参数的一个例外，它的结果是发起例外codice_253；具有实际参数的形式：，这里的codice_253是接受实际参数的一个例外，而codice_94是可赋值给codice_253的实际参数类型的一个表达式，它的结果是引发具有配对实际参数codice_94的例外codice_253。
Modula-3的例外处理，基于了codice_301语句。采用类似的块系统的有Delphi、Python、Scala和Visual Basic.NET。 codice_301语句有如下形式：
TRY
 Body
EXCEPT 
 id_1 (v_1) =&gt; Handler_1
 | id_n (v_n) =&gt; Handler_n
 ELSE Handler_0 
END
这里的codice_632和每个codice_633都是语句，每个codice_25指名一个例外，而每个codice_534是一个标识符。codice_636和每个codice_519都是可选的。每个codice_519声明一个变量，其类型是例外codice_639的实际参数类型，而它的作用域是codice_640。如果codice_519缺席，形式codice_642，是codice_643的简写。
codice_644子句执行codice_632。如果结果是正常的，则忽略codice_646等子句。如果codice_632引发任何列出的例外codice_639，则执行codice_640。在处理具有配对实际参数codice_94的例外codice_639的时候，codice_534在执行codice_640之前被初始化为codice_94。如果codice_632引发任何其他例外，并且codice_636存在，则执行它。在这两种情况的任何之一下，codice_301语句的结果是选出的处理器的结果。如果codice_632引发未列出的例外，而codice_636缺席，则codice_301语句的结果是codice_632引发的例外。
Modula-3还提供codice_662语句，它有如下形式：，它执行语句codice_663接着执行语句codice_664。如果codice_663的结果是正常的，则codice_644语句的结果等价于codice_467。如果codice_663的结果是例外，并且codice_664的结果是正常，则在codice_664执行之后，重新发起来自codice_663的例外。如果二者的结果都是例外，则codice_644语句的结果是来自codice_664的例外。
在Modula-3中，和语句的语义，分别被定义为发起叫做codice_13和codice_14的例外。codice_13不接受实际参数，codice_14接受任意类型的实际参数，程序不能显式的命名这些例外。语句，非正式的等价于：
TRY
 S; S; S; ...
EXCEPT 
 exit-exception =&gt; (*skip*)
END
过程.
过程类型.
一个过程要么是codice_66，要么是有如下构成的三元组：
形式参数有如下形式：，这里的：
形式，是的简写。
codice_298形式参数绑定到对应的实际参数所指示的变量上，也就是说它是别名。对于codice_298形式参数，实际参数必须是可写的指定式，它的类型同于形式参数的类型，或者在codice_298数组形式参数的情况下，它可赋值给这个形式参数。
codice_299形式参数绑定到具有未用的位置的一个变量上，并且它被初始化为实际参数对应的值。对于codice_299或codice_303形式参数，实际参数是可赋值给形式参数类型的任何表达式，并且放开了针对赋值局部过程的禁止。
codice_303形式参数，如果实际参数是指定式并且有与形式参数相同的类型，或者是可赋值给形式参数的类型的一个数组类型，则被当作codice_298形式参数对待，它的只读语义体现在指定这个指定式为只读的，否则被当作codice_299形式参数对待。
在签名中，如果省略了codice_711，则这个过程是真正（proper）过程，否则为函数过程；如果省略了codice_712，则假定它为codice_713。一个过程发起未包括在codice_393集合中的一个例外，是一个必查的运行时间错误，如果语言实现将这个运行时间错误映射成一个例外，则这个例外隐含的包括在所有的codice_393子句中。
过程声明.
有两种形式的过程声明：只允许出现在接口中的，和只允许出现在模块中的，这里的codice_25是一个标识符，codice_717是过程签名，而codice_389是一个块。在一个模块的最外层作用域内声明的过程是顶层过程，其他过程是局部过程。局部过程可以作为形式参数传递但不能被赋值，因为在栈实现下，局部过程在包含它的栈桢被弹出后会成为无效的。
过程类型声明有如下形式：，这里的codice_717是签名规定。一个过程codice_591是过程类型codice_7的一个成员，或称为是它的一个值的条件为，它是codice_66，或者它的签名被codice_7的签名所含盖（cover）。这里的签名codice_724含盖签名codice_725的条件是：
过程常量是声明为过程的一个标识符。过程变量是声明为具有过程类型的一个变量。例如：
PROCEDURE P(txt: TEXT := "P") = 
BEGIN
 IO.Put(txt)
END P;
VAR q: PROCEDURE(txt: TEXT := "Q") := P;
声明了过程常量codice_591和过程变量codice_731。两个过程如果有一致的闭包，就是说它们涉及相同的过程体和环境，则二者相等即有相同的值。形式参数名字和缺省值，影响一个过程的类型即签名，而非这个过程的值。将一个过程常量赋值给一个过程变量，改变这个它的值，而非它的类型。比如上例中有：codice_732为codice_174。缺省的形式参数的解释，由一个过程的类型来确定，而非这个过程的值。比如上例子中：codice_734打印codice_591，而codice_736打印codice_737。
过程调用.
过程调用有如下形式：codice_738，这里的codice_591是一个过程取值的表达式，而codice_281是关键字或位置绑定的一个列表。关键字绑定有如下形式：codice_741，这里的codice_742是一个表达式，而codice_743是一个标识符。位置绑定有如下形式：codice_742，这里的codice_742是一个表达式。当关键字和位置绑定混合在一个调用中的时候，位置绑定必须前导于关键字绑定。如果绑定列表为空，圆括号仍然是需要的。
绑定列表要如下这样重写，来适合codice_591的类型签名：首先，每个位置绑定codice_742，若为codice_281中的第codice_113个绑定，则通过补充上第codice_113个形式参数的名字，转换成关键字绑定增加到关键字绑定列表中。其次，对于有缺省值并且在第一步之后没有被绑定的每个形式参数，将它的形式参数的名字codice_743和它的缺省值codice_79，形成关键字绑定codice_753增加到关键字绑定列表。重写成的关键字绑定列表必须只绑定形式参数，并且必须只绑定每个形式参数正好一次。
执行过程调用，需要求值过程取值表达式codice_591和它的实际参数，绑定形式参数，并执行过程体。codice_591和它的实际参数的求值次序是未定义的。调用一个未定义或codice_66过程，是一个必查的运行时间错误。调用具有过程体codice_389的真正过程，在绑定了实际参数之后等价于：
TRY
 B
EXCEPT
 return-exception =&gt; (*skip*)
END
而调用具有过程体codice_389的函数过程等价于：
TRY
 B; (错误：没有返回值)
EXCEPT
 return-exception (v) =&gt; (结果成为v)
END
递归定义.
对于常量、类型或过程声明codice_759、变量声明codice_760、例外声明codice_761或揭示codice_759，如果codice_763出现在codice_27的任何部份展开之中，则它是递归的。对于省略了类型的变量声明codice_765，如果codice_763出现在codice_349的类型codice_27的任何部份展开中，则它是递归的。允许这种声明的条件，是codice_763在codice_27的任何部份展开中所有的出现为如下三者之一：在类型构造子codice_190或codice_772的某个出现之内，在类型构造子codice_773的一个字段或方法类型之内，或者在一个过程体之内。下面是合法的递归声明的例子：
TYPE 
 RealList = REF RECORD
 val: REAL;
 link: RealList
 END;
 ListNode = OBJECT
 link: ListNode
 END;
 IntList = ListNode OBJECT
 val: INTEGER
 END;
 T = PROCEDURE(n: INTEGER; p: T);
EXCEPTION E(PROCEDURE() RAISES {E});
PROCEDURE P(b: BOOLEAN) =
BEGIN
 IF b THEN P(NOT b) END 
END P;
特征性技术.
Modula-3的设计者认为Modula-2最成功的特征，是提供了在模块之间的显式接口，故而接口在Modula-3中保留而在本质上没有变化。到模块的接口，是披露了模块公开部份的声明搜集，而未声明于接口中的模块中的东西是私有的。模块导入它所依赖的接口，而导出它所实现的接口（在Modula-3中可以是多个接口）。
在现代编程语言的实现下，一个抽象类型的值，被表示一个对象，它的操作由叫做对象的方法的过程值的一个套件来实现。一个新的对象类型，可以定义为现存类型的子类型，在这种情况下新类型拥有旧类型的所有方法，并且可能还有新的方法（继承）。新类型可以为旧方法提供新的实现（覆盖）。对象类型和Modula-2的的组合，产生了一个新事物：部份不透明（partially opaque）类型，这里的一个对象的一些字段在一个作用域内是可见的，而其他的是隐藏的。
泛型模块是一种模板，在其中一些导入的接口被当作形式参数，它们在实例化泛型的时候要绑定到实际接口。不同的泛型实例是独立编译的，源代码程序被重复使用，而编译后的代码对不同实例通常是不同的。为保持简单性，在Modula-3中泛型被限制在模块层面，泛型的过程和类型不孤立存在，并且泛型形式参数必须都是完全的接口。
将计算分解入并发进程（或线程）中是关注点分离的基本方法。Modula-2提供的线程很弱，本质上相当于协程。Modula-3采用了霍尔的监视器，它是并发编程的更坚实基础。在设计中加入了简化的Mesa同步原语，它足够成功而在实质上没有改变的结合入了Modula-3。
一个语言特征是不安全的，如果它的滥用可以破坏运行时系统，使得程序的进一步运行不忠实于语言的语义，不安全特征的一个例子，是没有边界检查的数组赋值。一个安全的程序的一个错误，可以导致计算中止，并给出运行时间错误信息或给出错误解答，但它不会导致计算分崩离析。Modula-3在这个领域里跟从Cedar，接受只在显式的标记为不安全的模块中才允许的少量不安全特征。
模块化.
接口中的声明，除了任何变量初始化必须是常量，和过程声明必须只能指定签名而不能有过程体之外，同于在块中的声明。接口有如下形式：
INTERFACE id;
 Imports;
 Decls
END id.
这里的codice_25是命名这个接口的标识符，codice_775是一序列的导入语句，而codice_15是一序列的声明。在codice_15中的名字和可见的导入的名字必须是各不相同的。两个或更多个接口形成导入环是静态错误。典型的接口定义，通常为每个接口定义一个数据结构（记录或对象）以及任何的支持过程。
模块除了实体名字的可见性之外，就像是一个块。一个实体在一个块中是可见的，如果它被声明在这个块中，或在某个包围的块中；而一个实体在一个模块中是可见的，如果它被声明在这个模块中，或在这个模块所导入或导出的一个接口中。模块有如下形式：
MODULE id EXPORTS Interfaces;
 Imports;
Block id.
这里的codice_25是命名这个模块的一个标识符，codice_779是这个模块导出的那些接口的不同名字的一个列表，codice_775是一序列的导入语句，而codice_781是一个块，它是模块体。名字codice_25必须重复于终结模块体的codice_783之后。codice_784可以省略，在这种情况下codice_779缺省为codice_25，即模块将缺省导出相同名称的接口。Modula-3中的导出codice_2，与Modula-2中的导出codice_788无关，它在概念上对应于Modula-2的“实现”。
如果模块codice_789导出了接口codice_349，则在codice_349中声明的所有名字在codice_789中是不加以限定而可见的。一个模块codice_789导出一个接口codice_349，可以重新声明在这个接口中声明的一个或多个过程，为其提供过程体。在codice_789中的签名，必须被在codice_349中的签名所含盖。要确定在对这个过程的调用中的关键字绑定的解释，在codice_789内使用codice_789中的签名，在其他地方使用codice_349中的签名。除了重新声明导出的过程之外，在codice_781的顶层中声明的名字，可见的导入的名字，和在导出的接口中声明的名字必须是各不相同的。
一个程序的模块和接口的名字叫做全局名字。查找全局名字的方法是依赖于实现的，比如通过文件系统查找路径。一个模块或接口codice_801导入一个接口codice_349，从而使得在codice_349中声明的实体和揭示在codice_801中可见，但不包括codice_349所导入的那些。一个模块codice_789使用了接口codice_349，如果codice_789导入或导出了codice_349，或者codice_789使用了导入codice_349的一个接口。Modula-3中所有的编译单元，要么是接口要么是模块。任何编译单元都可以使用codice_812语句从其他接口导入标识符。与其他语言的包含功能不同，任何标识符都可以追踪到它的起源位置。
codice_812语句有如下的典型使用形式：，它导入全局名字为codice_349的接口，并给它一个同样的局部名字，从而使用点表示法访问接口中的实体（类似于访问记录中的字段）。比如接口中的主要类型按惯例命名为codice_7，则使用它时采用的限定形式为codice_816。如果导入接口的名字与模块内的其他实体的名字发生冲突，则可以使用这样的形式给与它不同的局部名字。
codice_812语句还有如下的另一种形式：，它为在接口codice_349中声明为codice_763的实体，介入一个局部名字codice_763，比如：，并且不加限定的使用它，比如：。局部绑定优先于全局绑定，例如：，为全局名字为codice_821的实体介入了局部名字codice_763。非法使用相同的局部名字两次是静态错误。
一个程序是模块和接口的搜集，包含了任何它的模块或接口所导入或导出的所有接口，并且在其中没有重复定义的过程、模块或接口。执行一个程序的效果是执行它的所有模块的模块体。模块的执行次序受到初始化规则的约束：如果模块codice_789依赖于模块codice_763并且codice_763不依赖于codice_789，则codice_763的模块体将先于codice_789的模块体执行。这里的模块codice_789依赖于模块codice_763，如果M使用了codice_763导出的一个接口，或者codice_789所依赖的一个模块依赖于codice_763。
其模块体最后执行的模块叫做主模块。在初始化规则不能唯一的确定它的情况下，语言实现提供了指定这个主模块的方法，即主模块是导出codice_1的模块，它的内容是依赖于实现的。程序执行在主模块终止的时候终止，即使并发的控制线程仍在执行。
下面是接口与模块的一个规范性的例子，是有隐藏表示的一个公开堆栈：
INTERFACE Stack;
 TYPE T &lt;: REFANY;
 PROCEDURE Create(): T;
 PROCEDURE Push(VAR s: T; x: REAL);
 PROCEDURE Pop(VAR s: T): REAL;
END Stack.
MODULE Stack;
 REVEAL T = BRANDED OBJECT
 item: REAL;
 link: T
 END;
 PROCEDURE Create(): T = 
 BEGIN 
 RETURN NIL 
 END Create;
 PROCEDURE Push(VAR s: T; x: REAL) =
 BEGIN 
 s := NEW(T, item := x, link := s)
 END Push;
 PROCEDURE Pop(VAR s: T): REAL =
 VAR res: REAL;
 BEGIN 
 res := s.item;
 s := s.link;
 RETURN res
 END Pop;
BEGIN
END Stack.
如果多于一个模块需要此堆栈的表示，则应当把它转移到私有接口中，从而在需要它的任何地方的都可以导入它：
INTERFACE Stack;
 (* ... 同上 ... *)
END Stack.
INTERFACE StackRep;
 IMPORT Stack; 
 REVEAL Stack.T = BRANDED OBJECT
 item: REAL;
 link: Stack.T
 END
END StackRep.
MODULE Stack;
 IMPORT StackRep;
 (* Push、Pop和Create同上 *)
BEGIN
END Stack.
对象类型.
一个对象要么是codice_66，要么是到有配对的方法套件的一个数据记录的引用，方法套件是接受这个对象作为第一个实际参数的那些过程的一个记录。这个数据记录可以包含经由对象类型的子类型介入的增加字段，这个套件可以包含经由子类型介入的增加方法。对象赋值是引用赋值。要复制一个对象的数据记录到另一个对象，这些字段必须单独的赋值。
Modula-3意图保持采用对象这个最简单的术语，而非在其他面向对象语言中对应的术语类。对象类型声明采用如下形式：
TYPE T = ST OBJECT
 Fields
 METHODS
 Methods
 OVERRIDES
 Overrides
 END 
这里的codice_836是可选的超类型，如果省略则缺省为codice_80，codice_838是完全如同记录类型那样的字段声明的一个列表，codice_839是方法声明的一个列表，而codice_840是方法覆盖的一个列表。在字段和方法声明中介入的名字，必须相互不同，并且不同于在覆盖声明中的名字。codice_7的字段构成自codice_836的字段和随后的新声明的字段。codice_7的方法构成自经过codice_844中的覆盖修改的codice_836的方法，和随后的codice_846中声明的方法。codice_7有着同codice_836一样的引用类。 
关键字codice_773可选的可以前导上codice_193或codice_191来给对象加铭牌，使之唯一而避免结构等价，这里的codice_172是文本常量。其含义同于非对象的引用类型，codice_172被省略时，系统自动生成唯一性的一个字符串。
方法声明有如下形式：codice_854，这里的codice_355是个标识符，codice_717是过程签名，而codice_857是顶层过程常量。方法声明指定了codice_7的方法codice_355有签名codice_717和值codice_857。如果省略了codice_862假定它为codice_863。如果codice_857非空，它的第一个形式参数必须是缺省的模态codice_299，并有着codice_7的某个超类型的类型（包括且经常是codice_7自身），并且去除了第一个形式参数结果就是codice_717所含盖的签名。
方法覆盖（override）有如下形式：codice_869，这里的codice_355是超类型codice_836的方法的名字，而codice_857是顶层过程常量。方法覆盖指定了codice_7的方法codice_355是codice_857，并非指定codice_876。如果codice_857非空，它的第一个形式参数必须是缺省的模态codice_299，并有着codice_7的某个超类型的类型（包括且经常是codice_7自身），去除了codice_857第一个形式参数结果就是codice_836的codice_355方法所含盖的签名。举例说明：
TYPE 
 Super = OBJECT
 a: INTEGER;
 METHODS
 p()
 END;
 Sub = Super OBJECT
 b: INTEGER
 END;
PROCEDURE ProcSuper(self: Super) = ... ; 
PROCEDURE ProcSub(self: Sub) = ... ;
这里的过程codice_884和codice_885是类型codice_886和codice_887的对象的codice_888方法的候选值。例如：
如果codice_7是对象类型或到记录的引用，codice_203操作有如下形式：，这里的codice_281是用来初始化新字段的关键字绑定的一个列表，不允许位置绑定。每个绑定codice_892将字段codice_338初始化为值codice_58。如果codice_7是对象类型，则codice_281还可以包括形如codice_897的方法覆盖，这里的codice_355是codice_7的一个方法而codice_591是一个顶层过程常量。是的语法糖。
如果codice_345是一个对象，则codice_331指示在codice_345的数据记录中名为codice_338的数据字段。codice_331是可写的指定式，它的类型是这个字段的所声明的类型。如果codice_355是codice_345方法之一，以codice_908形式的过程调用，指示执行codice_345的codice_355方法，这等价于：codice_911。
如果codice_7是对象类型而codice_355是codice_7的方法的名字之一，则codice_334指示codice_7的codice_355方法。它的过程类型的第一个实际参数有类型codice_7，而第一个实际参数对应的形式参数名字是未指定的，因此在调用codice_334时，第一个实际参数必须按位置来指定，不能用关键字。这种表示法便于子类型方法调用它的某个超类型的对应方法。
在子类型中的字段或方法遮盖（mask）了在超类型中的任何同名的字段或方法。要访问被遮盖的字段，使用类型运算codice_920来将子类型变量当作超类型的成员，这里的codice_7必须是对象类型或有跟踪的引用类型，而codice_94必须可赋值给codice_7。codice_432在检查了codice_94是codice_7一个成员之后返回codice_94，如果检查失败则发生运行时间错误。
下面的例子展示了在声明新方法和覆盖现存方法之间的不同，首先声明如下：
TYPE
 Super = OBJECT 
 METHODS
 m() := ProcSuper
 END;
 SubOverridden = Super OBJECT
 OVERRIDES
 m := ProcSub
 END;
 SubExtended = Super OBJECT
 METHODS
 m() := ProcSub
 END;
VAR
 a := NEW(Super); 
 b := NEW(SubOverridden);
 c := NEW(SubExtended);
然后可以用codice_928激活codice_929，用codice_930激活codice_931，用codice_932激活codice_933，如此调用在覆盖和扩展之间没有区别。但是扩展的codice_934的方法套件有两个方法，而覆盖的codice_172的方法套件只有一个方法，这可以通过使用codice_920将子类型的变量codice_172和codice_934视为超类型codice_886的成员来披露：codice_940激活codice_931，codice_942激活codice_943。
对象不能，因为在语言实现中，一个对象变量的静态类型不确定它的数据记录的类型。对象类型确定了关乎数据记录字段和方法套件的超类型链的前缀的那些类型。
实现.
下面是语言设计者提出的对象的一种可能实现的梗概，一个对象可以表示为它的数据记录的第一个字的地址。前面的字存储一个对象头部，它包含一个唯一于对象类型的类型代码。这些类型代码是小型整数，对每个对象类型和每个有跟踪的引用类型都有一个代码。在对象头部之前的字，存储到这个对象的方法套件的一个引用。如果这个对象没有方法，这个字可以省略。这还允许对象共享方法套件，这是常见情况。如果codice_345是一个对象，codice_945是它的数据字段之一，而codice_355是它的方法之一，则在这种表示下：
这里假定了字段和方法以自然方式表示为偏移量。进一步的问题是如何高效的测试一个对象codice_345是否有着类型codice_7，这是codice_287和codice_100语句所需要的。codice_287的最简单实现，是维护一个以类型代码为索引的数组codice_952，codice_953是其类型代码为codice_954的对象类型的超类型的类型代码，如果它没有超类型则为codice_66。要测试codice_345是否是一个codice_7，使用一个循环来计算codice_7的类型代码是否出现在如下序列之中：
TYPECODE(o), st[TYPECODE(o)], st[st[TYPECODE(o)]], ... NIL
这个序列可以称为codice_345的类型的超类型路径，而它的长度可称为codice_345的类型的深度。利用上每个类型的深度都是编译时间确定的，因此可以同相应的类型代码一起存储的事实，可以有更快速的codice_287实现。如果codice_7的类型代码，出现在类型codice_8的超类型路径上，它就该在位置codice_964上。如果每个类型的超类型路径都是顺序数组，这意味着codice_287可以用恒定时间实现。因为超类型路径通常不太长，这是一个有吸引力的策略。在不常见的对象类型有非常长的超类型链的情况下，只有这个链的直到某个极大长度的一个前缀，会被顺序的存储。在运行时间，这个深度差如果超出这个链的顺序存储的长度，实现必须回退到链表。
不透明类型及其披露.
（opaque）类型声明有如下形式：，这里的codice_7是标识符，而codice_8是指示一个引用类型的表达式。它将名字codice_7介入为不透明类型，并披露了codice_8是codice_7的超类型。
Modula-3的揭示（revelation）机制，将关于一个不透明类型的信息，介入到一个作用域之内，不同于其他声明，揭示不介入新的名字。不同的作用域，可以披露（reveal）关于一个不透明类型的不同的信息。例如，某个不透明类型，在一个作用域内知晓为codice_67的子类型，在其他的作用域内可以知晓为codice_80的子类型。
不透明类型名字所指示的实际类型叫做具体类型。codice_7的具体类型，必须披露于程序的其他地方。在对象声明中，如果codice_836被声明为不透明类型，则只在知晓codice_836的具体类型为对象类型的作用域内，codice_7的声明是合法的。如果codice_7被声明为不透明类型，则只在完全知晓codice_7的具体类型，或者知晓它为对象类型的作用域内，codice_203是合法的。
揭示分为两种：部份的和完全的，一个程序可以包含一个不透明类型的任意多个部份揭示，但必须包含唯一的一个完全揭示。
部份揭示有如下形式：，这里的codice_131是类型表达式（可能就是个名字），而codice_7是被声明为不透明类型的一个标识符（可能有限定）。它披露了codice_131是codice_7的超类型。在任何作用域内，对一个不透明类型披露的超类型，必须是在子类型关系下是线性有序的。就是说，如果披露了codice_984和codice_985，则必须也要披露要么codice_986要么codice_987。是合法的非递归声明。
完全揭示有如下形式：，这里的codice_131是类型表达式（不能就是个名字），它的最外层类型构造子，是有铭牌的一个引用或对象类型，而codice_7是被声明为不透明类型的一个标识符（可能有限定）。这个揭示指定了codice_131是codice_7的具体类型。在任何作用域内，披露为codice_7的超类型的任何类型，都必须是codice_131的超类型，否则是一个静态错误。不同的不透明类型有不同的具体类型，因为codice_131包含了一个铭牌并且在程序中所有的铭牌都是独一的。是非法的递归声明。
揭示只允许用在接口和模块的最外层作用域内。在接口中的揭示可以被导入到任何需要它的作用域之内。揭示提供了向客户隐藏实现细节的，在概念上简单清晰却非常强力的机制，例如：
INTERFACE I; 
 TYPE T &lt;: ROOT;
 PROCEDURE P(x: T): T;
END I.
INTERFACE IRep;
 IMPORT I;
 REVEAL I.T = MUTEX BRANDED OBJECT
 count: INTEGER
 END;
END IRep.
INTERFACE IClass;
 IMPORT I;
 REVEAL I.T &lt;: MUTEX;
END IClass.
导入codice_349的编译单元见到的codice_816是codice_80的不透明子类型，因而被限制为可以分配类型codice_816的对象，把它们传递给codice_999，或声明codice_816的子类型。导入codice_1001的编译单元见到的codice_816是具体类型，它是有扩展的codice_1003字段的codice_1004的子类型。导入codice_1005的编译单元见到的codice_816是codice_1004的不透明子类型，因而可以锁定类型codice_816的对象。
泛型.
在泛型接口或模块中，某些导入的接口名字被当作形式参数，它们在泛型被实例化的时候要绑定到实际接口上。泛型接口及其实例有如下左侧的形式，并等价于右侧定义的普通接口：
这里的codice_1009是命名这个泛型接口的一个标识符，codice_1010是叫做codice_1009的形式导入的标识符的一个列表，而codice_632同在非泛型接口中一样，是一序列导入和随后的一序列声明。这里的codice_349是这个实例的名字，而codice_1014是codice_1009的形式导入要绑定的实际接口的一个列表。
泛型模块及其实例有如下左侧的形式，并等价于右侧定义的普通模块：
这里的codice_1009是命名这个泛型模块的一个标识符，codice_1010是叫做codice_1009的形式导入的标识符的一个列表，而codice_632同在非泛型模块中一样，是一序列的导入和随后的一个块。这里的codice_349是这个实例的名字，codice_27是由codice_789导出的接口的一个列表，而codice_1014是codice_1009的形式导入要绑定的实际接口的一个列表。codice_1025可以省略，在这种情况下它缺省为codice_1026。泛型模块自身没有导出，它们只在被实例化时提供。
泛型接口及其对应的泛型模块（与C++模板一样）可以轻松定义和使用抽象数据类型，但粒度在模块级别，裸露的类型codice_51或codice_63不能使用，相比之下，它们在C++模板中可以使用。例如，可以定义泛型堆栈：
GENERIC INTERFACE Stack(Elem);
 (* 这里的Elem.T不是开放数组类型 *)
 TYPE T &lt;: REFANY;
 PROCEDURE Create(): T;
 PROCEDURE Push(VAR s: T; x: Elem.T);
 PROCEDURE Pop(VAR s: T): Elem.T;
END Stack.
GENERIC MODULE Stack(Elem);
 REVEAL T = BRANDED OBJECT
 n: INTEGER;
 a: REF ARRAY OF Elem.T
 END;
 PROCEDURE Create(): T =
 BEGIN
 RETURN NEW(T, n := 0, a := NIL) 
 END Create;
 PROCEDURE Push(VAR s: T; x: Elem.T) =
 BEGIN
 IF s.a = NIL THEN 
 s.a := NEW(REF ARRAY OF Elem.T, 5)
 ELSIF s.n &gt; LAST(s.a^) THEN
 WITH temp = NEW(REF ARRAY OF Elem.T, 2 * NUMBER(s.a^)) DO
 FOR i := 0 TO LAST(s.a^) DO
 temp[i] := s.a[i] 
 END;
 s.a := temp
 END
 END;
 s.a[s.n] := x;
 INC(s.n)
 END Push;
 PROCEDURE Pop(VAR s: T): Elem.T =
 BEGIN
 DEC(s.n);
 RETURN s.a[s.n] 
 END Pop;
BEGIN
END Stack.
然后使用接口例如codice_1029，对其进行实例化，只要这个接口定义了泛型模块所需的属性即可：
INTERFACE IntegerElem;
 TYPE T = INTEGER;
END IntegerElem.
INTERFACE IntStack = Stack(IntegerElem) END IntStack.
MODULE IntStack = Stack(IntegerElem) END IntStack.
面向对象.
Modula-3下的面向对象编程，经常采用部份不透明类型，它通过如下惯用法来声明：，这里的不透明类型codice_7，支持在随后定义的对象类型中的字段和方法，而不会披露codice_7的确切结构，也不披露codice_7可能支持的其他方法。
下面的例子，定义一个接口codice_1033，具有按习惯约定命名的两个类型，导出类型codice_7，和公开对象类型codice_1035，这里声明了codice_7是codice_1035的不透明子类型，codice_1035具有两个方法codice_1039和codice_1040；随后是codice_1033接口的完全实现：
INTERFACE Person;
 TYPE T &lt;: Public;
 Public = OBJECT 
 METHODS
 getAge(): INTEGER;
 init(name: TEXT; age: INTEGER): T;
 END;
END Person.
MODULE Person;
 REVEAL T = Public BRANDED OBJECT 
 name: TEXT;
 age: INTEGER;
 OVERRIDES
 getAge := Age;
 init := Init;
 END;
 PROCEDURE Age(self: T): INTEGER =
 BEGIN
 RETURN self.age;
 END Age;
 PROCEDURE Init(self: T; name: TEXT; age: INTEGER): T =
 BEGIN
 self.name := name;
 self.age := age;
 RETURN self;
 END Init;
BEGIN
END Person.
要建立一个新的codice_1042对象，要使用内建操作codice_198，并调用方法codice_1040： 
VAR jim := NEW(Person.T).init("Jim", 25);
下面是整数堆栈的例子：
INTERFACE IntegerStack;
 TYPE
 T &lt;: Public OBJECT;
 Public = OBJECT
 METHODS
 init(): T;
 push(x: INTEGER);
 pop(): INTEGER;
 END;
END IntegerStack.
MODULE IntegerStack;
 REVEAL T = Public BRANDED OBJECT
 n: INTEGER;
 a: REF ARRAY OF INTEGER;
 OVERRIDES
 init := Init;
 push := Push;
 pop := Pop;
 END;
 PROCEDURE Init(self: T): T =
 BEGIN
 self.n := 0;
 self.a := NIL;
 RETURN self
 END Init;
 PROCEDURE Push(self: T; x: INTEGER) =
 BEGIN
 IF self.a = NIL THEN 
 self.a := NEW(REF ARRAY OF INTEGER, 5)
 ELSIF self.n &gt; LAST(self.a^) THEN
 WITH temp = NEW(REF ARRAY OF INTEGER, 2 * NUMBER(self.a^)) DO
 FOR i := 0 TO LAST(self.a^) DO
 temp[i] := self.a[i] 
 END;
 self.a := temp
 END
 END;
 self.a[self.n] := x;
 INC(self.n)
 END Push;
 PROCEDURE Pop(self: T): INTEGER =
 BEGIN
 DEC(self.n);
 RETURN self.a[self.n] 
 END Pop;
BEGIN
END IntegerStack.
创建新堆栈在这里的部份不透明类型实现下为：，在上述的不透明类型实现下为：。而压入一个数在这里的部份不透明类型实现下为：，在上述的不透明类型实现下为：。
多线程.
语言支持多线程和在线程间的同步。在运行时库（m3core）中有一个必要接口叫做codice_85，它支持采用分叉会合模型的多线程应用。这里还预定义不透明类型codice_1004被用来同步多个线程，并保护共享数据免于具有可能损害或竞争条件的同时访问。codice_1004是一个对象，因此可以从它派生其他对象。
codice_1048语句有如下形式：，这里的codice_16是一个语句，而codice_1050是一个表达式，它等价于：
WITH m = mu DO
 Thread.Acquire(m);
 TRY S FINALLY Thread.Release(m) END
END
这里的codice_355表示不出现在codice_16中的一个变量。codice_1048语句介入一个互斥锁要锁定的块，并暗含着在代码执行轨迹离开这个块时的解锁它。
不安全标记.
某些功能被认为是不安全的，编译器无法再保证结果是一致性的（例如，当与C编程语言交接时）。在codice_1054或codice_1055前面加上前缀关键字codice_1056，可用于告诉编译器启用语言的某些不安全的低级功能。例如，在codice_1056模块中，使用codice_290将整数的诸位元复制成浮点codice_63数，使用codice_1060释放无跟踪的内存。
一个接口是内在安全的，如果在安全模块中使用这个接口，无法产生不核查的运行时间错误。如果导出一个安全接口的所有的模块都是安全的，编译器保证这个接口的内在安全性。如果导出一个安全接口的任何模块是不安全的，就要编程者而非编译器，去做这种保证了。安全接口导入不安全接口，或安全模块导入或导出不安全接口，都是静态错误。导入不安全接口的编译单元本身必定也是不安全的。
对其他编程语言的影响.
尽管Modula-3没有获得主流地位，DEC-SRC M3发行的某些部份做到了。可能最有影响的一部份是网络对象库，它形成了Java包括了网络协议的最初的远程方法调用（RMI）实现的基础。在Sun从通用对象请求代理架构（CORBA）标准转移到基于IIOP的协议的时候才被放弃。Java关于远程对象的垃圾收集的文档仍提及了Modula-3网络对象为其先驱性工作 。
Python从Modula-3借鉴了模块系统、例外系统和关键字参数，Python的类机制受到C++和Modula-3的启发。Nim利用了Modula-3的某些方面比如有跟踪和无跟踪的指针。