编辑“︁
Modula-3
”︁
跳转到导航
跳转到搜索
警告:
您没有登录。如果您进行任何编辑,您的IP地址会公开展示。如果您
登录
或
创建账号
,您的编辑会以您的用户名署名,此外还有其他益处。
反垃圾检查。
不要
加入这个!
{{Infobox programming language | name = Modula-3 | logo = Modula-3.svg | logo size = 160px | paradigm = [[指令式编程|指令式]], [[过程式编程|过程式]], [[结构化编程|结构化]], [[模块化编程|模块化]], [[并发计算|并发]] | typing = [[強弱型別|强类型]], [[类型系统|静态]], 安全或在不安全时显式安全隔离 | scope = [[作用域|词法]] | family = [[Niklaus Wirth|Wirth]] [[Modula]] | designers = {{le|Luca Cardelli}}, James Donahue, Lucille Glassman, Mick Jordan, Bill Kalsow, {{le|Greg Nelson|Greg Nelson (computer scientist)}}<ref name=DecSrcRefMan95>[http://www.minet.uni-jena.de/www/fakultaet/schukat/Inf1/Praktikum/Modula-3-refman.ps Modula-3 Reference Manual] {{Wayback|url=http://www.minet.uni-jena.de/www/fakultaet/schukat/Inf1/Praktikum/Modula-3-refman.ps |date=20070716111705 }} Luca Cardelli, James Donahue, Lucille Glassman, Mick Jordan, Bill Kalsow, Greg Nelson. [[DEC Systems Research Center]] (SRC) (February 1995)</ref> | developers = [[DEC]], [[Olivetti]], {{Not a typo|elego}} Software Solutions GmbH | released = {{Start date and age|1988}} | implementations = {{le|DEC系统研究中心|DEC Systems Research Center|SRC}} Modula-3, CM3<ref>{{Cite web |url=https://modula3.elegosoft.com/cm3/ |title=Critical Mass Modula-3 (CM3) |website=Critical Mass Modula-3 |publisher=elego Software Solutions GmbH |access-date=2020-03-21 |archive-date=2020-09-24 |archive-url=https://web.archive.org/web/20200924195221/https://modula3.elegosoft.com/cm3/ |dead-url=no }}</ref>, PM3<ref>{{cite web |url=https://modula3.elegosoft.com/pm3/ |title=Polytechnique Montréal Modula-3 (PM3): What is it |author=<!--Unstated--> |date=<!--Undated--> |website=Polytechnique Montréal Modula-3 |publisher=elego Software Solutions GmbH |access-date=2020-03-21 |archive-date=2020-11-19 |archive-url=https://web.archive.org/web/20201119220633/https://modula3.elegosoft.com/pm3/ |dead-url=no }}</ref>, EZM3<ref>{{cite web |url=http://www.cvsup.org/ezm3/ |title=Ezm3: An Easier Modula-3 Distribution |last=Polstra |first=John D. |date=November 9, 2006 |website=CVSup.org |url-status=dead |archive-url=https://web.archive.org/web/20070429162224/http://www.cvsup.org/ezm3/ |archive-date=2007-04-29 |access-date=2020-03-21 }}</ref>, M3/PC Klagenfurt<ref>{{cite web |url=http://www.ifi.uni-klu.ac.at/Modula-3/m3pc/m3pc.html |title=M3/PC Klagenfurt 96: a Modula-3 environment for MS-DOS |last=Weich |first=Carsten |date=<!-- Undated. --> |website=Department of Informatics |publisher=University of Klagenfurt |url-status=dead |archive-url=https://web.archive.org/web/20000520073936/http://www.ifi.uni-klu.ac.at/Modula-3/m3pc/m3pc.html |archive-date=2000-05-20 |access-date=2020-03-21 }}</ref> | influenced by = [[ALGOL]], {{le|Euclid (编程语言)|Euclid (programming language)|Euclid}}, [[Mesa语言|Mesa]], [[Modula-2]], {{le|Modula-2+}}, [[Oberon]], [[Object Pascal]] | influenced = [[C♯|C#]], [[Java]], [[Nim]]<ref>{{Cite web |url=http://nim-lang.org/question.html |title=Frequently Asked Questions |last=Picheta |first=Dominik |last2=Locurcio |first2=Hugo |date=<!-- Undated. --> |access-date=2020-03-21 |archive-date=2016-12-10 |archive-url=https://web.archive.org/web/20161210163951/http://nim-lang.org/question.html |dead-url=no }}</ref>, [[Python]]<ref>{{Cite web |url=https://www.python.org/doc/essays/foreword/ |title=Programming Python: Foreword (1st ed.) |last=van Rossum |first=Guido |date=May 1996 |website=Python.org |quote=besides ABC, my main influence was Modula-3. This is another language with remarkable elegance and power, designed by a small, strong-willed team (most of whom I had met during a summer internship at DEC's Systems Research Center in Palo Alto). |access-date=2020-03-21 |archive-date=2014-07-24 |archive-url=https://web.archive.org/web/20140724173913/https://www.python.org/doc/essays/foreword/ |dead-url=no }}</ref>, {{le|Baby Modula-3}}<ref>[https://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-95.html Baby Modula-3 and a theory of objects] {{Wayback|url=https://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-95.html |date=20210513033723 }} {{le|Martin Abadi|Martín Abadi}}. [[Digital Equipment Corporation]] (DEC) [[DEC Systems Research Center|Systems Research Center]] (SRC) Research Report 95 (February 1993).</ref> | platform = [[IA-32]], [[x86-64]], [[PowerPC]], [[SPARC]] | operating system = [[跨平台]]: [[FreeBSD]], [[Linux]], [[Darwin (操作系统)|Darwin]], [[SunOS]] | website = {{URL|www.modula3.org}} }} '''Modula-3'''是一种[[系统编程]][[编程语言|语言]],它是叫做{{le|Modula-2+}}的升级版本的[[Modula-2]]的后继者。虽然它已经在研究界有了影响力,受其影响的语言有Java、C#和[[Python]]<ref>{{Cite web |url=https://docs.python.org/3/faq/design.html#why-self |title=Design and History FAQ - Why must ‘self’ be used explicitly in method definitions and calls? |quote=The idea was borrowed from Modula-3. It turns out to be very useful, for a variety of reasons. |date=March 21, 2020 |website=Python.org |access-date=2020-03-21 |archive-date=2012-10-24 |archive-url=https://web.archive.org/web/20121024164243/http://docs.python.org/faq/design.html#why-self }}</ref>,但未能在工业上被广泛采用。它是在1980年代末由在[[数字设备公司]](DEC){{le|DEC系统研究中心|DEC Systems Research Center|系统研究中心}}(SRC)和[[Olivetti]]研究中心(ORC)的{{le|Luca Cardelli}}、James Donahue、Lucille Glassman、Mick Jordan(之前在Olivetti软件技术实验室工作)、Bill Kalsow和{{le|Greg Nelson|Greg Nelson (computer scientist)}}设计。 Modula-3的主要特点,是保持系统编程语言的强力,同时具有简单性和安全性。Modula-3意图延续[[Pascal语言|Pascal]]的[[类型安全]]和Modula-2的[[模块化编程]]传统,同时为实际编程引入新构造。特别是Modula-3添加了如下支持:[[例外处理 (编程)|例外处理]]、[[命名参数|关键字参数]]与缺省参数值、有跟踪的[[参照|引用]]与[[垃圾回收 (计算机科学)|垃圾回收]]、[[面向对象编程|面向对象]]、不透明类型及其披露、[[泛型编程|泛型]]、[[线程|多线程]]和不安全代码显式标记。Modula-3的设计目标,是以非常基本的形式,实现指令式编程语言的大部份重要的现代特征。因此省略了涉嫌危险和复杂的特征,如[[多重继承]]和[[运算符重载]]。 {{TOClimit|limit=3}} ==发展历史== Modula-3项目始于1986年11月,当时[[莫里斯·威尔克斯]]向[[尼克劳斯·维尔特]]写信,提出了一些关于Modula新版本的想法。威尔克斯在此之前一直在[[DEC]]工作,他回到英格兰并加入了[[Olivetti]]的研究策略委员会。Wirth已经转移到了[[Oberon]],但Wilkes的团队在Modula名下继续开发没有任何问题。语言定义于1988年8月完成,并于1989年11月更新了版本<ref>{{Cite web|url=https://www.cs.purdue.edu/homes/hosking/m3/reference/m3.html|title=Modula-3: Language definition.|access-date=2021-06-25|archive-date=2021-06-25|archive-url=https://web.archive.org/web/20210625044322/https://www.cs.purdue.edu/homes/hosking/m3/reference/m3.html}}</ref>。DEC和Olivetti的编译器很快就出现了,随后又有了第三方实现。 它的设计受到{{le|DEC系统研究中心|DEC Systems Research Center|SRC}}和[[艾康電腦|Acorn计算机]]研究中心(ARC,在Olivetti收购Acorn之后叫做ORC)在{{le|Modula-2+}}语言上的工作的很大影响<ref>{{Cite report |last1=Rovner |first1=Paul |last2=Levin |first2=Roy |last3=Wick |first3=John |date=January 11, 1985 |title=SRC-RR-3 On extending Modula-2 for building large, integrated systems |url=https://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-3.html |website=Hewlett-Packard Labs |access-date=2021-08-11 |archive-date=2021-05-16 |archive-url=https://web.archive.org/web/20210516080555/https://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-3.html }}</ref>,{{le|Modula-2+}}是编写{{le|DEC Firefly}}多处理器[[VAX]]工作站的操作系统的语言<ref>{{Cite report |last1=Thacker |first1=Charles P. |author1-link=Charles P. Thacker |last2=Stewart |first2=Lawrence C. |last3=Satterthwaite |first3=Edwin H. Jr. |date=December 30, 1987 |title=Firefly: a multiprocessor workstation SRC-RR-23 |url=https://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-23.html |website=Hewlett-Packard Labs |access-date=2021-08-11 |archive-date=2021-05-16 |archive-url=https://web.archive.org/web/20210516161122/https://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-23.html }}</ref><ref>{{Cite report |last1=McJones |first1=Paul R. |last2=Swart |first2=Garret F. |date=September 28, 1987 |title=Evolving the Unix system interface to support multithreaded programs SRC-RR-21 |url=https://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-21.html |website=Hewlett-Packard Labs |access-date=2021-08-11 |archive-date=2021-05-16 |archive-url=https://web.archive.org/web/20210516161109/https://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-21.html }}</ref>,还是在基于[[ARM架构|ARM]]的{{le|Acorn Archimedes}}系列计算机的{{le|ARX操作系统|ARX (operating system)}}项目中,ARC编写用于Acorn C和模块执行库(CAMEL)的Acorn编译器的语言<ref name=DecSrcRr052/>。正如修订的Modula-3报告所述<ref name=DecSrcRr052>[https://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-52.html Modula-3 report (revised)] {{Wayback|url=https://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-52.html |date=20210628234936 }} Luca Cardelli, James Donahue, Lucille Glassman, Mick Jordan, Bill Kalsow, Greg Nelson. [[DEC Systems Research Center]] (SRC) Research Report 52 (November 1989)</ref>,该语言演化自[[Mesa语言|Mesa]]、[[Modula-2]]、Cedar和{{le|Modula-2+}},还受到其他语言的影响如:[[Object Pascal]],[[Oberon]]和{{le|Euclid (编程语言)|Euclid (programming language)|Euclid}}。 在20世纪90年代,Modula-3作为一种[[教育编程语言列表|教学语言]]获得了可观的传播<ref>{{cite web|url=https://www.cl.cam.ac.uk/~pr10/publications/plc93.pdf|title=From ML to C via Modula-3 - an approach to teaching programming|author={{le|Peter Robinson|Peter Robinson (computer scientist)}}|year=1994|quote=The Computer Science course at the [[University of Cambridge]] teaches [[ML语言|ML]] as an introductory language at the beginning of the freshman year, and then uses Modula-3 to introduce imperative programming at the end of that year. Further lectures on advanced features of Modula-3 are given early in the second year, together with separate lectures on [[C语言|C]]. Other, specialised languages are introduced subsequently as the course progresses.|access-date=2021-08-11|archive-date=2021-12-17|archive-url=https://web.archive.org/web/20211217025701/https://www.cl.cam.ac.uk/~pr10/publications/plc93.pdf}}</ref>,[[华盛顿大学]]在1994年发行的[[微内核]][[操作系统]]{{le|SPIN (操作系统)|SPIN (operating system)|SPIN}}是用Modula-3开发的<ref>{{cite web|url=http://www-spin.cs.washington.edu/external/overview.html|title=SPIN OVERVIEW|quote=SPIN and its extensions are written in Modula-3, a type-safe programming language developed at DEC SRC. Modula-3 offers modern language features such as objects, garbage collection, and threads. We rely on its type-safe properties to protect sensitive kernel data and interfaces from malicious or errant extensions.|access-date=2021-08-11|archive-date=2022-02-24|archive-url=https://web.archive.org/web/20220224211519/http://www-spin.cs.washington.edu/external/overview.html}}</ref>,但它从未广泛用于工业用途。造成这种情况的原因,可能是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系统编程》中<ref>[https://archive.org/details/systems-programming-with-modula-3 Systems Programming with Modula-3.]</ref>,收录了设计者对四个设计要点的讨论:结构等价与名称等价、子类型规则、泛型模块和参数模态。Modula-3现在主要是在大学中的比较编程语言课程中教授,其教科书已绝版。 ==程序示例== Modula-3中所有的程序至少具有一个模块文件,而大多数程序还包括一个接口文件,客户端使用它来访问来自模块的数据。语言语法的一个常见示例是[[Hello world]]程序: <syntaxhighlight lang="modula2"> MODULE Main; IMPORT IO; BEGIN IO.Put("Hello World\n") END Main. </syntaxhighlight> 与其他语言一样,Modula-3程序必须有导出<code>Main</code>接口的实现模块,如上述示例中实现模块缺省导出了同名的接口,它可以处在名为Main.m3的文件中;或者是通过<code>EXPORTS</code>导出<code>Main</code>接口的实现模块,比如: <syntaxhighlight lang="modula2"> MODULE HelloWorld EXPORTS Main; IMPORT IO; BEGIN IO.Put("Hello World\n") END HelloWorld. </syntaxhighlight> 建议这个模块所在的文件的名字与实际模块名字相同,当它们不同时编译器只是发出一个警告。 ==语言设计理念== Modula-3委员会将Modula-3设计为[[ALGOL]]语言家族的现代代表,他们认为: *始自[[BCPL]]的[[C语言|C]]语言家族,其优美在于以适度的代价得到了相对[[汇编语言]]的巨大飞跃,但它们接近于目标机器的低层{{le|编程模型|Programming model}}是天然有危险性的;其中的衍生语言[[C++]],通过增加[[对象 (计算机科学)|对象]]丰富了C,但它放弃了C的最佳优点简单性,却没有消除它最差缺点即低层编程模型。 *另一个极端是[[LISP]]语言家族,它们有着混合了[[lambda演算]]和[[列表构造函数|配对函数]]理论的{{le|编程模型|Programming model}},但它们趋于难以高效实现,因为在编程模型中值的统一对待,招致了所有的值都由指针统一表示的运行时间系统;这个家族的语言的良好实现避免了多数代价而能用于[[系统编程]],但它们普遍秉性仍旧向往[[动态内存分配|堆分配]]而非[[调用栈|栈分配]],这种运行时间系统经常将它们孤立于封闭环境中,而不能容纳用其他语言写的程序。 *[[ALGOL]]语言家族处在这两个极端之间,它的现代代表包括[[Pascal语言|Pascal]]、[[Ada]]、[[Modula-2]]等,这些语言拥有的编程模型反映了[[随机存取机]]在工程上的约束,隐藏了任何特定于机器的细节。它们放弃了[[LISP]]家族的优美和数学对称性,使得无须特殊技巧就可能高效的实现,并且避免了[[C语言|C]]家族的多数危险和依赖机器的特征。 [[ALGOL]]家族语言都有[[强类型]]系统,其基本想法是把值空间划分成类型,限制变量持有单一类型的值,并且限制[[运算]]应用于固定类型的[[运算数]]。这个语言家族的趋势,在1960年代是朝向控制流程和数据结构特征,在1970年代是朝向{{le|信息隐藏 (编程)|Information hiding|信息隐藏}}特征如接口、不透明类型和泛型,在1980年代是采纳来自[[LISP]]家族和始自[[BCPL]]的[[C语言|C]]家族的仔细挑选的技术。 Modula-3委员会坚持一个自称为“复杂度预算”的设计原则:在五十页内将语言准确而易读的描述出来,为此只容纳最有用的特征。精选出来的特征直接针对两个主要目标: *[[介面 (資訊科技)|接口]]、[[对象 (计算机科学)|对象]]、[[泛型编程|泛型]]和[[线程]],提供有助于建造大型程序的基础[[架构模式|抽象模式]]; *不安全代码的显式标记、自动[[垃圾收集 (计算机科学)|垃圾收集]]和[[例外处理 (编程)|例外]],有助于让程序更加安全和[[健壮性 (计算机科学)|健壮]]。 这些特征中任何一个都没有特殊的新颖性,但是组合起来是简单而强力的。人们越是更好的理解程序,就越是使用更大的建造[[块 (编程)|块]]去构造它。[[指令]]之后是[[语句 (计算机科学)|语句]],语句之后是[[子程序|过程]],过程之后是[[介面 (資訊科技)|接口]],下一步应当是[[抽象类型]]。在理论层面,抽象类型,是通过其操作的规定,而非通过其数据的表示,来定义的类型。 ==词法记号== [[关键字]]: {| | *<code>AND</code> *<code>ANY</code> *<code>ARRAY</code> *<code>AS</code> *<code>BEGIN</code> *<code>BITS</code> *<code>BRANDED</code> *<code>BY</code> *<code>CASE</code> *<code>CONST</code> *<code>DIV</code> | *<code>DO</code> *<code>ELSE</code> *<code>ELSIF</code> *<code>END</code> *<code>EVAL</code> *<code>EXCEPT</code> *<code>EXCEPTION</code> *<code>EXIT</code> *<code>EXPORTS</code> *<code>FINALLY</code> *<code>FOR</code> | *<code>FROM</code> *<code>GENERIC</code> *<code>IF</code> *<code>IMPORT</code> *<code>IN</code> *<code>INTERFACE</code> *<code>LOCK</code> *<code>LOOP</code> *<code>METHODS</code> *<code>MOD</code> *<code>MODULE</code> | *<code>NOT</code> *<code>OBJECT</code> *<code>OF</code> *<code>OR</code> *<code>OVERRIDES</code> *<code>PROCEDURE</code> *<code>RAISE</code> *<code>RAISES</code> *<code>READONLY</code> *<code>RECORD</code> *<code>REF</code> | *<code>REPEAT</code> *<code>RETURN</code> *<code>REVEAL</code> *<code>ROOT</code> *<code>SET</code> *<code>THEN</code> *<code>TO</code> *<code>TRY</code> *<code>TYPE</code> *<code>TYPECASE</code> *<code>UNSAFE</code> |style="vertical-align: top"| *<code>UNTIL</code> *<code>UNTRACED</code> *<code>VALUE</code> *<code>VAR</code> *<code>WHILE</code> *<code>WITH</code> |} 保留[[标识符]]: {| | *<code>ABS</code> *<code>ADDRESS</code> *<code>ADR</code> *<code>ADRSIZE</code> *<code>BITSIZE</code> *<code>BOOLEAN</code> | *<code>BYTESIZE</code> *<code>CARDINAL</code> *<code>CEILING</code> *<code>CHAR</code> *<code>DEC</code> *<code>DISPOSE</code> | *<code>EXTENDED</code> *<code>FALSE</code> *<code>FIRST</code> *<code>FLOAT</code> *<code>FLOOR</code> *<code>INC</code> | *<code>INTEGER</code> *<code>ISTYPE</code> *<code>LAST</code> *<code>LONGINT</code> *<code>LONGREAL</code> *<code>LOOPHOLE</code> | *<code>MAX</code> *<code>MIN</code> *<code>MUTEX</code> *<code>NARROW</code> *<code>NEW</code> *<code>NIL</code> | *<code>NULL</code> *<code>NUMBER</code> *<code>ORD</code> *<code>REAL</code> *<code>REFANY</code> *<code>ROUND</code> | *<code>SUBARRAY</code> *<code>TEXT</code> *<code>TRUE</code> *<code>TRUNC</code> *<code>TYPECODE</code> *<code>VAL</code> |} [[算子 (编程)|运算符]]: {| style="text-align:center" |- |style="width: 50px;"|<code>+</code>||style="width: 50px;"|<code><</code>||style="width: 50px;"|<code>#</code> |style="width: 50px;"|<code>=</code>||style="width: 50px;"|<code>;</code>||style="width: 50px;"|<code>..</code>||style="width: 50px;"|<code>:</code> |- |<code>-</code>||<code>></code>||<code>{</code>||<code>}</code>||<code><nowiki>|</nowiki></code>||<code>:=</code>||<code><:</code> |- |<code>*</code>||<code><=</code>||<code>(</code>||<code>)</code>||<code>^</code>||<code>,</code>||<code>=></code> |- |<code>/</code>||<code>>=</code>||<code>[</code>||<code>]</code>||<code>.</code>||<code>&</code>|| |} 此外,[[注释 (计算机语言)|注释]]是开于<code>(*</code>并合于<code>*)</code>的任意字符的序列。注释可以嵌套并可以扩展跨越多于一行。 ==基础性语法和语义== Modula-3承袭了[[ALGOL]]家族传统的[[强类型]],并且为了使[[类型系统]]尽可能的统一,采用了如下原则: *首先,类型或目标类型确定是没有歧义的,所有的[[表达式]]的类型由它的子表达式来确定,而非由它的使用来确定。 *其次,没有自动[[类型转换|转换]]。 *第三,可赋值性和类型兼容性,是依据单一的在语法上用[[算子 (編程)|算符]]<code><:</code>指定的[[子类型]][[关系 (数学)|关系]]来定义的,它具有如下性质:如果<code>T</code>是<code>U</code>的[[子类型]],则所有的<code>T</code>的成员都是<code>U</code>的[[元素 (数学)|成员]],子类型关系是[[自反关系|自反]]和[[传递关系|传递]]的;子类型关系是处理具有[[继承 (计算机科学)|继承]]的[[对象 (计算机科学)|对象]]所需要的,但也能用于确定常规类型的兼容性规则。 一个典型的不安全运行时间错误,是释放仍可由活跃引用([[迷途指针|悬空指针]])触及的数据结构。避免这个问题的唯一可靠方式,是不再触及存储的自动释放,或称为[[垃圾收集 (计算机科学)|垃圾收集]]。Modula-3因而提供了有跟踪的[[參照|引用]],它类似于[[Modula-2]]的[[指针]],除了它们所指向的存储被保存在有跟踪的堆之中之外,在这里它们将在到它们的引用都消失时自动释放。垃圾收集的另一个巨大利益是简化了接口,没有垃圾收集,接口就必须规定客户或实现是否有责任释放每个分配的引用,和在什么条件下可以安全的去这么做。 [[例外处理 (编程)|例外]]是一次退出多层[[作用域]]的[[控制结构]]。一个例外被重复的发起退出当前活跃的作用域,直到找到给这个例外的处理器(handler),并将控制权移交给这个处理器,如果没有处理器,则计算以某种依赖于系统的方式终止,比如进入[[调试器]]。在Modula-3中,<code>EXIT</code>和<code>RETURN</code>语句的语义,分别使用退出例外<code>exit-exception</code>和返回例外<code>return-exception</code>来定义。Modula-3的设计者认为语言实现,除了这种返回例外和退出例外之外,更应当以各种例外为代价,加速正常的结果产出;为每个引发的例外花费一千条指令,来为每个过程节约一条指令都是合理的。 ===块与声明=== [[声明 (编程)|声明]]介入一个常量、类型、变量、例外或过程的一个名字。这个名字的[[作用域]]是包含这个声明的[[块 (编程)|块]]。一个块可以出现为一个模块或过程的本体,或一个块[[语句 (计算机科学)|语句]],[[Pascal语言|Pascal]]和[[Modula-2]]没有块语句<ref>{{cite web|title=Modula-2 Reference - Statements|url=https://www.modula2.org/reference/statements.php|access-date=2023-02-21|archive-date=2023-02-21|archive-url=https://web.archive.org/web/20230221111935/https://www.modula2.org/reference/statements.php|dead-url=no}}</ref>。一个块有如下形式: <syntaxhighlight lang="modula2"> Decls BEGIN S END </syntaxhighlight> 这里的<code>Decls</code>是成序列的声明,而<code>S</code>是一个语句,经常是顺序复合语句即成序列的语句,它是这个块的执行部份。这里的<code>Decls</code>与<code>S</code>在[[缩进风格|缩进]]上对齐,是因为在[[Pascal语言|Pascal]]语言家族中,可能含有嵌套过程声明的声明序列的位置相较于[[ALGOL 60]],从紧随在关键字<code>BEGIN</code>之后转移到了紧靠在其前面。一个块的声明可以介入一个名字最多一次,然而一个名字可以在嵌套的块中重新声明。在块中这些声明的次序,除了确定变量的初始化次序之外无关紧要。 一个Modula-3程序规定了一个计算,它作用在一序列的叫做[[記憶體位址|地点]](location)的数字元件之上。[[变量 (程序设计)|变量]]是地点的一个集合,它依据这个变量的[[类型系统|类型]]所确定的约定,表示一个数学上的{{le|值 (数学)|Value (mathematics)|值}}。如果一个值可以由类型<code>T</code>的某个变量来表示,则称谓这个值是<code>T</code>的一个成员,并且<code>T</code>包含这个值。 [[声明 (编程)|声明]]为一个[[变量 (程序设计)|变量]]、[[类型系统|类型]]、[[子程序|过程]]、{{le|常量 (计算机编程)|Constant (computer programming)|常量}}或[[例外处理 (编程)|例外]]指定一个被称为[[标识符 (计算机语言)|标识符]]的{{le|符号 (编程)|Symbol (programming)|符号}}作为名字。一个声明应用于其上的程序区域,叫做这个声明的[[作用域]]。作用域是可以嵌套的。一个标识符的含义,由这个标识符在其中声明的最小的包围作用域来确定。 [[类型系统|类型]]声明有如下形式:{{code|2="modula2"|1=TYPE T = U}},这里的<code>T</code>是一个标识符,而<code>U</code>是一个类型或类型表达式。在需要类型的各种地方,通常允许类型表达式。 [[变量 (程序设计)|变量]]声明有如下形式:{{code|2="modula2"|1=VAR id: T := E}},这里的<code>id</code>是一个标识符,<code>T</code>是除了开放数组之外的一个非空类型,而<code>E</code>是一个表达式。它声明了<code>id</code>是类型<code>T</code>的一个变量,其初始值是<code>E</code>的值。<code>:= E</code>和<code>: T</code>任何一个都可省略,但二者不能都省略。如果省略了<code>T</code>,它采用<code>E</code>的类型。如果省略了<code>E</code>,初始值是类型<code>T</code>的任意值。如果二者都存在,<code>E</code>必须可赋值给<code>T</code>。初始值是一种简写,等价于在随后的[[块 (编程)|块]]的可执行部份开始处插入赋值<code>id := E</code>,如果多个变量有初始值,按它们声明的次序插入它们的赋值。形式{{code|2="modula2"|1=VAR v_1, ..., v_n: T := E}},是{{code|2="modula2"|1=VAR v_1: T := E; ...; VAR v_n: T := E}}的简写。 {{le|常量 (计算机编程)|Constant (computer programming)|常量}}声明有如下形式:{{code|2="modula2"|1=CONST id: T = C}},这里的<code>id</code>是一个标识符,<code>T</code>是一个类型,而<code>C</code>是一个常量表达式。它声明<code>id</code>为具有类型<code>T</code>和值<code>C</code>的一个常量。<code>: T</code>可以省略,在这种情况下<code>id</code>的类型是<code>C</code>的类型。如果<code>T</code>存在,则它必须包含<code>C</code>。 ===值与类型=== Modula-3的[[类型系统]]使用{{le|结构类型系统|Structural type system|结构等价}},而非[[Modula-2]]的{{le|名称类型系统|Nominal type system|名称等价}}即类型构造子的每次出现产生一个新类型。在结构等价中,在如果两个类型的定义在展开的时候,也就是在所有常量表达式都被替换为它们的[[值 (计算机科学)|值]],并且所有类型名字都被替代为它们的定义的时候,变成相同的,则这两个类型是相同的。在[[递归数据类型]]的情况下,这个展开是部份展开的[[无穷]][[极限 (数学)|极限]]。 有三种[[序数]]类型:[[枚举]]、[[区间|子范围]]和[[整数 (计算机科学)|整数]]类型,预定义了如下序数类型:<code>INTEGER</code>、<code>LONGINT</code>、<code>[[基数 (数学)|CARDINAL]]</code>(如同子范围<code>[0..LAST(INTEGER)]</code>)、<code>[[真值|BOOLEAN]]</code>(枚举<code>{FALSE, TRUE}</code>)和<code>[[字符 (计算机科学)|CHAR]]</code>(包含至少256个元素表示[[ISO/IEC 8859-1|ISO-Latin-1]]代码的枚举)。[[整数]]和[[枚举]][[元素 (数学)|元素]]合起来叫做序数[[值 (计算机科学)|值]]。序数值<code>v</code>如果是整数或扩展精度整数,则<code>v</code>的基础类型分别是<code>INTEGER</code>或<code>LONGINT</code>,否则<code>v</code>的基础类型是包含它的那个唯一性的枚举类型。两个序数如果有相同的值则二者相等。 有三种浮点类型:<code>[[实数|REAL]]</code>、<code>LONGREAL</code>和<code>EXTENDED</code>,这些类型的属性规定在对应的必要接口中。两个[[浮点数]]如果定义它们的底层实现相同则二者相等。 一个[[参照|引用]]值要么是<code>[[空指针|NIL]]</code>,要么是一个叫做所引用者(referent)的变量的[[地址空间|地址]]。预定义了如下[[参照|引用]]类型:<code>REFANY</code>,它包含所有的有跟踪的引用,<code>ADDRESS</code>,它包含所有的无跟踪的引用,和<code>[[空指针|NULL]]</code>,它只包含<code>[[空指针|NIL]]</code>。Modula-3支持在[[执行期|运行时间]]的数据[[内存分配|分配]]。分配操作有如下形式:{{code|2="modula2"|1=NEW(T, ...)}},这里的<code>T</code>是除了预定义的<code>REFANY</code>、<code>ADDRESS</code>或<code>NULL</code>之外的引用类型。两个[[参照|引用]]如果[[地址空间|寻址]]相同的[[記憶體位址|地点]]则二者相等。 [[记录]]是叫做这个记录的[[字段]]的命名[[变量 (程序设计)|变量]]的一个[[序列]],记录类型的声明有如下形式:{{code|2="modula2"|1=TYPE T = RECORD FieldList END}},在这里的类型表达式中,{{le|算符 (计算机科学)|Operator (computer programming)|算子}}<code>RECORD</code>是[[类型构造子]],而它的实际参数是字段声明的一个列表<code>FieldList</code>,其中每个字段都有如下形式:{{code|2="objectpascal"|1=fieldName: Type := default}},这里的字段名字<code>fieldName</code>是一个标识符,而字段类型<code>Type</code>是除了开放数组之外的非空类型,而字段缺省值<code>default</code>是一个常量表达式。形式{{code|2="objectpascal"|1=f_1, ..., f_m: Type := default}},是{{code|2="objectpascal"|1=f_1: Type := default; ...; f_m: Type := default}}的简写。两个记录如果有相同的字段,并且对应的字段都相等则二者相等。 在Modula-3中的[[对象 (计算机科学)|对象]],非常类似于[[Simula|Simula 67]]中的对象:它们总是引用,它们拥有数据[[字段]]和[[方法 (计算机科学)|方法]]二者,并且它们有着单一[[继承 (计算机科学)|继承]]而非[[多重继承]]。预定义了如下[[对象 (计算机科学)|对象]]类型:<code>ROOT</code>,它是没有字段或方法的有跟踪的对象类型,和<code>UNTRACED ROOT</code>,它是没有字段或方法的无跟踪的对象类型。 [[不透明数据类型|不透明类型]]是一个名字,它指示某个给定[[引用类型]]的未知[[子类型]]。预定义了两个[[不透明数据类型|不透明类型]]:<code>TEXT <: REFANY</code>和<code>MUTEX <: ROOT</code>,它们分别表示[[文本]][[字符串|串]]和[[互斥锁|互斥]][[信号量]],它们的属性分别规定于必要接口<code>Text</code>和<code>Thread</code>之中。 [[数组]]是叫做这个数组的[[元素 (数学)|元素]]的组成[[变量 (程序设计)|变量]]的有索引的[[集合 (计算机科学)|搜集]]。有两种数组类型,固定的和开放的。固定数组的长度是在编译时间确定的,开放数组的长度是在运行时间分配确定的,这个长度此后不能改变。两个数组如果有相同的长度,并且对应的元素都相等则二者相等。 [[位段|压缩]]类型的变量出现在记录、对象或数组中,占据指定数量的[[位元]],并毗邻于前导的字段或元素紧压打包。压缩类型的声明有如下形式:{{code|2="modula2"|1=TYPE T = BITS n FOR Base}},这里的<code>Base</code>是一个基础类型,而<code>n</code>是整数取值的常量表达式。 [[集合 (数学)|集合]]是取自某个序数类型的[[值 (计算机科学)|值]]的[[集合 (计算机科学)|搜集]]。集合类型声明有如下形式:{{code|2="modula2"|1=TYPE T = SET OF Base}},这里的基础类型<code>Base</code>是一个序数类型。两个集合如果有相同的元素则二者相等。 不包含值的一个类型是空的,例如<code>[1..0]</code>是一个空类型。空类型可以用来建造非空类型,比如<code>SET OF [1..0]</code>。声明一个空类型的一个变量是静态错误。 所有的{{le|表达式 (计算机科学)|Expression (computer science)|表达式}}都有一个唯一的类型,但是一个[[值 (计算机科学)|值]]可以是很多类型的一个[[元素 (数学)|成员]],例如值<code>6</code>是<code>[0..9]</code>和<code>INTEGER</code>二者的成员,因此论述“一个值的类型”将会是有歧义的。短语“<code>x</code>的类型”,意味着“表达式<code>x</code>的类型”;然而短语“<code>x</code>是<code>T</code>的成员”,意味着“<code>x</code>的值是<code>T</code>的成员”。但在有一种情况下,一个值可以称为有着一个类型:所有的[[对象 (计算机科学)|对象]]或有跟踪的[[参照|引用]]值,包括了一个类型代码,它叫做这个引用值的分配类型。分配类型通过<code>TYPECASE</code>语句来测试。 语言规定提供了类型运算<code>BITSIZE()</code>、<code>BYTESIZE()</code>和<code>ADRSIZE()</code>,分别返回变量<code>x</code>或者类型<code>T</code>的那些变量的[[位元]]数目、8位[[字节]]的数目和可[[地址空间|寻址]][[記憶體位址|地点]]的数目。在所有情况下,<code>x</code>必须是指定式,而<code>T</code>必须不是开放数组类型,指定式<code>x</code>只在它的类型是开放数组的时候求值。 ====序数类型==== [[枚举]]类型声明有如下形式:{{code|2="modula2"|1=TYPE T = {id_1, id_2, ..., id_n} }},这里的<code>id</code>是各不相同的标识符,类型<code>T</code>是<code>n</code>个值的[[全序关系|有序集合]],表达式<code>T.id_i</code>指示这个类型在递增次序下的第<code>i</code>个值,空枚举<code>{ }</code>是允许的。 [[区间|子范围]]类型声明有如下形式:{{code|2="modula2"|1=TYPE T = [Lo..Hi]}},这里的<code>Lo</code>和<code>Hi</code>是有相同的基础类型的两个序数值,<code>T</code>的值是从<code>Lo</code>到<code>Hi</code>含二者所有的值,它们必须是常量表达式,如果<code>Lo</code>超过<code>Hi</code>,则子范围是空的。 每个不同的枚举类型介入新的一组值,而子范围类型重复使用底层类型的值。例如: <syntaxhighlight lang="modula2"> TYPE T1 = {A, B, C}; T2 = {A, B, C}; U1 = [T1.A..T1.C]; U2 = [T1.A..T2.C]; (* sic *) V = {A, B} </syntaxhighlight> 这里的<code>T1</code>和<code>T2</code>是相同类型,因为它们有相同的展开定义。<code>U1</code>和<code>U2</code>也是相同的类型,因为<code>T1.C = T2.C</code>。类型<code>T1</code>和<code>U1</code>却是不同的,尽管它们包含相同的值,但是<code>T1</code>的展开定义是枚举,而<code>U1</code>的展开定义是子范围。类型<code>V</code>是第三个类型,它的值<code>V.A</code>和<code>V.B</code>无关于值<code>T1.A</code>和<code>T1.B</code>。 语言规定提供了支持[[序数]]类型的一些类型运算:对于序数类型<code>T</code>,<code>NUMBER(T)</code>返回在<code>T</code>中元素的数目,<code>FIRST(T)</code>返回<code>T</code>的最小值,<code>LAST(T)</code>返回<code>T</code>的最大值。<code>ORD(T.id)</code>将一个[[枚举]]的元素<code>T.id</code>,转换成表示它在枚举次序中所处位置的[[整数]],如果元素的类型是枚举<code>T</code>的子范围,结果是元素在<code>T</code>内的位置,而非在子范围内。<code>VAL(i, T)</code>将表示在枚举类型<code>T</code>中位置的[[整数]]<code>i</code>,转换成在这个[[枚举]]中此位置上的那个元素,如果<code>T</code>是枚举类型的子范围,结果是在源出枚举类型的位置<code>i</code>上的元素,而非在子范围内。如果<code>n</code>是类型<code>T</code>的一个[[整数]],则有[[平凡 (数学)|平凡]]的:<code>ORD(n) = VAL(n, T) = n</code>。 ====数组类型==== 固定[[数组]]类型声明有如下形式:{{code|2="modula2"|1=TYPE T = ARRAY Index OF Element}},这里的索引类型<code>Index</code>是序数类型,而元素类型<code>Element</code>是除了开放数组之外的任何类型。开放[[数组]]类型声明有如下形式:{{code|2="modula2"|1=TYPE T = ARRAY OF Element}},这里的元素类型<code>Element</code>是任何类型。 [[数组]]类型<code>T</code>的值,是元素类型为<code>Element</code>的数组;对于固定数组类型,这个数组的长度是索引类型<code>Index</code>的元素数目;对于开放数组类型,这个数组的长度是任意的;开放数组的索引类型是<code>INTEGER</code>子范围<code>[0..n-1]</code>,这里的<code>n</code>是这个数组的长度。开放数组类型只能用作形式参数的类型,引用类型的所引用者,另一个开放数组的元素类型,或作为数组[[构造器|构造子]]中的类型。 多维数组的形状是它每一维的长度的序列。更精确的说,一个数组的形状是它的长度跟随着任何它的元素的形状,非数组的形状是空序列。数组如果有相同的类型和形状,则它们是可赋值的。如果赋值的要么来源要么目标是开放数组,则需要运行时间检查。形如{{code|2="modula2"|1=ARRAY Index_1, ..., Index_n OF Element}}的表达式,是{{code|2="modula2"|1=ARRAY Index_1 OF ... OF ARRAY Index_n OF Element}}的简写。 如果<code>a</code>有数组类型<code>T</code>,则<code>a[i]</code>指示<code>a</code>的一个指定元素,它在数组中的位置对应于<code>i</code>在索引类型中位置。形如<code>a[i_1, ..., i_n]</code>的表达式,是<code>a[i_1]...[i_n]</code>的简写。索引的解释由一个数组的类型确定,而非它的[[值 (计算机科学)|值]];数组赋值改变数组变量的[[值 (计算机科学)|值]],而非它的类型。例如: <syntaxhighlight lang="modula2"> VAR a := ARRAY [1..3] OF REAL {1.0, 2.0, 3.0}; b: ARRAY [-1..1] OF REAL := a; </syntaxhighlight> 这里在<code>a</code>或<code>b</code>做元素值变更之前,<code>a = b</code>为<code>TRUE</code>,尽管<code>a[1] = 1.0</code>而<code>b[1] = 3.0</code>。 可以使用<code>NUMBER(A)</code>,得到一个固定数组类型的索引类型中的元素数目,使用<code>FIRST(A)</code>和<code>LAST(A)</code>,分别得到一个固定数组类型的索引类型的最小元素和最大元素。可以使用<code>NUMBER(a)</code>,得到一个数组的索引类型中的元素数目,使用<code>FIRST(a)</code>和<code>LAST(a)</code>,分别得到一个数组的索引类型的最小元素和最大元素,在这种情况下,表达式<code>a</code>只在它指示一个开放数组的时候求值。 ====引用类型==== 一个[[引用类型]]要么是有跟踪的要么是无跟踪的。当到已分配的一段存储的所有的有跟踪的引用都消失时,系统对这段存储进行[[垃圾回收 (计算机科学)|垃圾回收]]。有跟踪的[[引用类型]]以如下形式来声明:{{code|2="modula2"|1= TYPE T = REF Type}},这里的<code>Type</code>是任何类型。<code>T</code>的值是到类型<code>Type</code>的变量的有跟踪的引用,<code>Type</code>叫做<code>T</code>的所引用类型。无跟踪的引用类型以如下形式来声明:{{code|2="modula2"|1=TYPE T = UNTRACED REF Type}},这里的<code>Type</code>是任何无跟踪的类型(这个限制在不安全模块中解除)。 两个引用类型如果都是有跟踪的或都是无跟踪的,那么它们有相同的引用类。一个一般性的类型是有跟踪类型的条件为,它是有跟踪的引用类型,其任何字段类型是有跟踪类型的记录类型,其元素类型是有跟踪类型的数组类型,或其底层未压缩类型是有跟踪类型的压缩类型。 在有跟踪的和无跟踪的二者情况下,关键字<code>REF</code>可选的可以前导上<code>BRANDED b</code>,这里的<code>b</code>是叫做铭牌(brand)的文本常量。铭牌用来区分原本上相同的类型,它们没有其他语义效果。在一个程序中所有铭牌都必须是唯一性的。如果<code>BRANDED</code>出现而<code>b</code>缺席,系统自动的为<code>b</code>提供一个唯一的值。 [[动态内存分配|分配]]操作{{code|2="modula2"|1=NEW(T, ...)}}返回<code>T</code>的所引用类型的一个新分配的变量的地址,如果<code>T</code>是对象类型,则返回到有配对的方法套件的一个新分配的数据记录的引用。<code>NEW()</code>返回的引用不同于所有的现存引用。这个新引用的分配类型是<code>T</code>。如果<code>T</code>的所引用类型为空则是一个静态错误。如果<code>T</code>是到有<code>k</code>个开放维度的数组的引用,<code>NEW(T)</code>操作有如下形式:{{code|2="modula2"|1=NEW(T, n_1, ..., n_k)}},这里的这些<code>n</code>是指定新数组在它的前<code>k</code>个维度上的长度的整数取值表达式。 所有的对象类型和有跟踪的引用类型(包括<code>NULL</code>)都有一个关联的整数代码。不同的类型有不同的代码。给一个类型的代码对程序的任何单一执行都是常量,但对不同的执行可以不同。类型运算<code>TYPECODE(T)</code>,返回类型<code>T</code>的这个代码,而<code>TYPECODE(r)</code>,返回引用<code>r</code>的分配类型的这个代码。如果<code>T</code>是<code>REFANY</code>,或者不是对象类型或有跟踪的引用类型,则是一个静态错误。类型运算<code>ISTYPE(x, T)</code>,测试<code>x</code>是否为<code>T</code>的成员,这里的<code>T</code>必须是对象类型或有跟踪的引用类型,而<code>x</code>必须可赋值给<code>T</code>。 ===表达式=== {{le|表达式 (计算机科学)|Expression (computer science)|表达式}}规定产生一个值或变量的一个计算。所有的表达式都有一个静态确定的类型,它包含这个表达式可以产生的所有的值。在语法上,一个表达式要么是一个[[运算数]],要么是应用于自身也是表达式的实际参数的一个[[运算]]。运算数是[[标识符 (计算机语言)|标识符]]、{{le|文字 (计算机编程)|Literal (computer programming)|文字}}或[[类型系统|类型]]。通过递归的求值一个运算的实际参数并应用这个[[运算]],来[[求值策略|求值]]一个表达式。实际参数的求值次序,对除了[[短路求值]]的<code>AND</code>和<code>OR</code>之外的所有运算都是未定义的。 对一个[[子程序|过程]]的调用,在这个过程是返回一个结果的函数过程的时候,是称为函数应用的一个表达式,这个表达式的类型是过程的结果类型。 Modula-3语言规定提供了前缀算术运算:<code>+</code>、<code>-</code>,中缀算术运算:<code>+</code>、<code>-</code>、<code>*</code>、<code>/</code>、<code>DIV</code>、<code>MOD</code>,数值函数运算:<code>ABS(x)</code>、<code>FLOAT(x, T)</code>、<code>FLOOR(x)</code>、<code>CEILING(x)</code>、<code>ROUND(r)</code>、<code>TRUNC(r)</code>、<code>MAX(x, y)</code>、<code>MIN(x, y)</code>,关系运算:<code>=</code>、<code>#</code>、 <code><=</code>、<code>>=</code>、<code>></code>、<code><</code>,逻辑运算:<code>NOT p</code>、<code>p AND q</code>、<code>p OR q</code>,文本[[串接]]运算:<code>a & b</code>,内存分配运算:<code>NEW(T, ...)</code>,集合运算:[[并集]]<code>+</code>、[[补集#相对补集|差集]]<code>-</code>、[[交集]]<code>*</code>、[[对称差|对称差集]]<code>/</code>和[[元素 (数学)|隶属]]关系测试<code>e IN s</code>,它在序数<code>e</code>是集合<code>s</code>的元素的时候返回<code>TRUE</code>。 表达式可建构自如下{{le|文字 (计算机编程)|Literal (computer programming)|文字}}:{{le|整数文字|Integer literal}}、实数文字、{{le|字符文字|Character literal}}和{{le|字符串文字|String literal|文本文字}}。集合、数组和记录有[[构造器|构造子]]表达式: *<code>S{e_1, ..., e_n}</code>,这里的<code>S</code>是一个集合类型,而这些<code>e</code>是表达式或<code>lo..hi</code>形式的范围。这个构造子指示类型为<code>S</code>的一个值,它包含列出的那些值和在列出的范围中的那些值,这些<code>e</code>、<code>lo</code>和<code>hi</code>必须可赋值给类型<code>S</code>的元素类型。 *<code>A{e_1, ..., e_n}</code>,这里的<code>A</code>是一个数组类型,而这些<code>e</code>是表达式。这个构造子指示类型为<code>A</code>的一个值,它包含遵循列出次序的这些列出元素,这些<code>e</code>必须可赋值给<code>A</code>的元素类型。如果<code>A</code>是多维数组,则这些<code>e</code>必须自身也是数组取值表达式。如果<code>A</code>是固定数组类型,并且<code>n</code>至少为<code>1</code>,则<code>e_n</code>可以跟随着<code>, ..</code>,指出<code>e_n</code>的值按照需要重复任意多次来填满这个数组。 *<code>R{Bindings}</code>,这里的<code>R</code>是一个记录类型,而<code>Bindings</code>是完全同于过程调用的关键字或位置绑定的一个列表。这个绑定列表会被重写来适合<code>R</code>的字段和缺省值的列表,完全同于过程调用中的那些做为,记录字段名字扮演了过程形式参数的角色。这个构造子指示类型为<code>R</code>的一个值,它的字段值由重写后的这些绑定来指定。 {{le|常量 (计算机编程)|Constant (computer programming)|常量}}表达式是一般性的表达式类的子集,所要求的限制使得可以静态的求值这个表达式。在常量表达式中,除了<code>NEW()</code>、解引用(显式的或隐式的)、<code>SUBARRAY()</code>、<code>TYPECODE()</code>、<code>NARROW()</code>、<code>ISTYPE()</code>、<code>ADR()</code>和<code>LOOPHOLE()</code>之外的所有运算都是合法的,唯独可运用的过程,是在提供无符号[[字 (计算机)|字]]运算的<code>Word</code>接口中的函数。变量在常量表达式中,只能作为给<code>FIRST()</code>、<code>LAST()</code>、<code>NUMBER()</code>、<code>BITSIZE()</code>、<code>BYTESIZE()</code>或<code>ADRSIZE()</code>的实际参数出现,并且这个变量必须不是开放数组类型的。文字和顶层过程常量在常量表达式中是合法的。 ====指定式==== 产生变量的表达式叫做指定式(designator)。常量表达式永远不是指定式。指定式的类型是它产生的变量的类型。一个指定式依赖于[[上下文 (计算机)|上下文]],可以指示要么一个变量,要么这个变量的值。比如,它作为赋值的[[左值]]时是变量,作为[[右值]]时是这个变量的值。一些指定式是只读的,它们不能用在可能改变这个变量的值的上下文中,并非只读的指定式就是可写的指定式。 一个标识符是可写的指定式的条件为,它被声明为变量,是<code>VAR</code>或<code>VALUE</code>形式参数,<code>TYPECASE</code>或<code>TRY-EXCEPT</code>语句的局部变量,或绑定到可写指定式的<code>WITH</code>局部变量。一个标识符是只读的指定式的条件为,它是<code>READONLY</code>形式参数,<code>FOR</code>语句的局部变量,或绑定到非指定式或只读的指定式的<code>WITH</code>局部变量。只有如下[[运算]]可以产生指定式: *<code>r^</code>,这个运算叫做{{le|解引用算符|Dereference operator|解引用}}(dereferencing),它表示<code>r</code>的所引用者。表达式<code>r^</code>总是可写的指定式。如果<code>r</code>的类型是<code>REFANY</code>、<code>ADDRESS</code>、<code>NULL</code>、对象类型或不透明类型,则是一个静态错误;如果<code>r</code>是<code>NIL</code>,则是一个必查的运行时间错误。 *<code>a[i]</code>,这个运算叫做下标,它表示数组<code>a</code>的值的第<code>i + 1 - FIRST(a)</code>个元素,比如说<code>a</code>的索引类型是<code>[-1..1]</code>,<code>a[1]</code>是这个数组的值的第<code>1 + 1 - (-1)</code>个即第<code>3</code>个元素。表达式<code>a[i]</code>是否为指定式进而是否可写同于<code>a</code>。表达式<code>i</code>必须可赋值给<code>a</code>的索引类型。如果<code>a</code>是到数组的引用,则这里有隐含的解引用,即这里的<code>a[i]</code>是<code>a^[i]</code>的简写。 *<code>r.f</code>、<code>o.f</code>、<code>I.x</code>、<code>o.m</code>、<code>T.m</code>和<code>E.id</code>,这种[[点表示法]]运算叫选取(selection)。 **<code>r.f</code>表示记录<code>r</code>的指名字段<code>f</code>,表达式<code>r.f</code>是否为指定式进而是否可写同于<code>r</code>。如果<code>r</code>是到记录的引用,则这里有隐含的解引用,即<code>r.f</code>是<code>r^.f</code>的简写。 **<code>o.f</code>表示对象<code>o</code>的指名字段<code>f</code>,表达式<code>o.f</code>是可写的指定式。 **<code>I.x</code>表示导入的接口<code>I</code>的指名实体<code>x</code>,如果<code>x</code>被声明为变量,则表达式<code>I.x</code>是指定式并且总是可写的。 **<code>o.m</code>表示对象<code>o</code>的指名方法<code>m</code>,<code>o.m</code>不产生指定式。 **<code>T.m</code>表示对象类型<code>T</code>的指名方法<code>m</code>,<code>T.m</code>不产生指定式。 **<code>E.id</code>表示枚举类型<code>E</code>的值<code>id</code>,<code>E.id</code>不产生指定式。 *<code>SUBARRAY(a, from, for)</code>,它产生数组<code>a</code>的越过前<code>from</code>个元素并包含下<code>for</code>个元素的子数组。<code>SUBARRAY()</code>不复制数组,它是否为指定式进而是否可写同于<code>a</code>。如果<code>a</code>是多维数组,<code>SUBARRAY()</code>只应用于顶层数组。如果<code>from + for</code>超过了<code>NUMBER(a)</code>,则是一个必查的运行时间错误。 ===子类型规则=== 使用<code>T <: U</code>指示<code>T</code>是<code>U</code>的[[子类型]],也就是<code>U</code>是<code>T</code>的超类型。子类型规则是:如果<code>T <: U</code>,则类型<code>T</code>的所有值,都是类型<code>U</code>的值,反过来则不成立。 对于[[序数]]类型<code>T</code>和<code>U</code>,<code>T <: U</code>的条件为,它们拥有相同的基础类型,并且所有的<code>T</code>的成员都是<code>U</code>的成员。就是说序数类型上的子类型,反映了在值[[集合 (数学)|集合]]上的[[子集]]关系。 对于[[数组]]类型,一个数组类型<code>A</code>是一个数组类型<code>B</code>的子类型的条件为,它们有相同的最终元素类型,相同的维度数,并且对于每个维度,要么二者都是开放的,要么<code>A</code>是固定的而<code>B</code>是开放的,要么它们都是固定的并有着相同的大小。 对于[[子程序|过程]]类型,<code>T <: U</code>的条件为,它们除了形式参数名字、缺省值和<code>RAISES</code>集合之外是相同的,并且<code>T</code>的<code>RAISES</code>集合包含在<code>U</code>的<code>RAISES</code>集合中。<code>NIL</code>是所有的过程类型的一个成员。 对于[[位段|压缩]]类型,<code>BITS n FOR T</code>和<code>T</code>有相同的值,也就是说:<code>BITS n FOR T <: T</code>并且<code>T <: BITS n FOR T</code>。 对于[[集合 (数学)|集合]]类型<ref name="type" />,<code>SET OF A <: SET OF B</code>的条件是<code>A <: B</code>,就是说集合的子类型规则简单的使用值集合规则。 对于[[参照|引用]]类型有: <syntaxhighlight lang="objectpascal"> NULL <: REF T <: REFANY NULL <: UNTRACED REF T <: ADDRESS </syntaxhighlight> 就是说,<code>REFANY</code>和<code>ADDRESS</code>分别包含所有的有跟踪的和无跟踪的引用,<code>NIL</code>是所有的引用类型的一个成员。这两个规则也适用于加铭牌的类型。 对于[[对象 (计算机科学)|对象]]类型有: <syntaxhighlight lang="objectpascal"> ROOT <: REFANY UNTRACED ROOT <: ADDRESS NULL <: T OBJECT ... END <: T </syntaxhighlight> 就是说,所有对象都是引用,<code>NIL</code>是所有的对象类型的一个成员,而所有的子类型都包括在它的超类型之中。第三个规则也适用于加铭牌的类型。 对于所有的<code>T</code>,都有<code>T <: T</code>。对于所有的<code>T, U, V</code>,<code>T <: U</code>[[逻辑与|并且]]<code>U <: V</code>,[[蕴涵]]<code>T <: V</code>。就是说<code><:</code>是[[自反关系|自反]]和[[传递关系|传递]]的。但是<code>T <: U</code>并且<code>U <: T</code>,不蕴涵<code>T</code>与<code>U</code>是相同的,因为子类型关系不受形式参数名字、缺省值和压缩的影响。 ====可赋值性==== 一个类型<code>T</code>可[[赋值语句|赋值]](assignable)给类型<code>U</code>的条件是: *<code>T <: U</code>,或者 *<code>U <: T</code>并且<code>T</code>是一个数组或除了<code>ADDRESS</code>之外的一个引用类型(这个限制在不安全模块中解除),或者 *<code>T</code>和<code>U</code>是至少有一个共同成员的序数类型。 在第一种情况下,没有运行时间错误是有可能的。在第二种情况下,允许赋值一个<code>REFANY</code>到一个<code>REF T</code>,还允许赋值一个<code>ARRAY OF T</code>到一个<code>ARRAY I OF T</code>,这里分别要求对引用的类型和数组长度的运行时间检查;可以使用类型运算<code>NARROW(x, T): T</code>,将对象类型或有跟踪的引用类型的超类型变量当作其子类型的成员,这时需要运行时间检查,{{le|Modula-2+}}介入它就是为了以安全的方式,将<code>REFANY</code>变量赋值给确知的实际类型<code>REF T</code>。在第三种情况下,常规的范围检查就能确保特定的<code>T</code>的成员也是<code>U</code>的成员。 一个表达式<code>e</code>可赋值给一个变量<code>v</code>的条件是: *<code>e</code>的类型可赋值给<code>v</code>的类型,并且 *<code>e</code>的值是<code>v</code>的类型的一个成员,它不是一个局部过程,并且如果它是一个数组,则有同<code>v</code>一样的形状。 一个表达式<code>e</code>可赋值给类型<code>T</code>,如果<code>e</code>可赋值给类型<code>T</code>的某个变量。如果<code>T</code>不是一个开放数组,这等同声称<code>e</code>可赋值给类型<code>T</code>的任何变量。 ===语句=== 执行一个[[语句 (计算机科学)|语句]]产生一个计算,它可以正常产出结果(在[[可计算理论]]中称为[[停机问题|停机]]),发起一个例外,导致一个必查的运行时间错误,或[[无限循环]]。如果结果是一个例外,它可以可选的配对上一个实际参数。如果一个表达式作为一个语句的执行部份而求值,这个求值引发一个例外,则这个例外成为这个语句的结果。空语句是无操作的(no-op),在语言报告中写为{{code|2="modula2"|1=(*skip*)}}。 [[子程序|过程]]调用在这个过程是真正过程的时候是一个语句。调用一个函数过程并丢弃它的结果使用<code>EVAL</code>语句,它有如下形式:<code>EVAL e</code>,这里的<code>e</code>是一个表达式。它的效果是求值<code>e</code>并忽略其结果。<code>RETURN</code>语句用于从过程中返回。 [[赋值语句]]有如下形式:{{code|2="modula2"|1=v := e}},这里的<code>v</code>是可写的指定式,而<code>e</code>是可赋值给<code>v</code>所指示变量的表达式。赋值语句将<code>v</code>设置为<code>e</code>的值,<code>v</code>和<code>e</code>的求值次序是未定义的,但<code>e</code>会在<code>v</code>更新之前求值。特别是,如果<code>v</code>和<code>e</code>是有交叠的子数组,以没有元素在被用作来源之前被用作目标的方式进行赋值。 顺序复合语句有如下形式:<code>S_1; S_2</code>,它在[[Modula-2]]中称为“语句序列”。一些编程者使用分号作为语句终结符(terminator),另一些编程者将它用作分隔符(separator);Modula-3允许这两种风格,语言报告将其用作分隔符。 [[块 (编程)|块]]语句有如下形式:{{code|2="modula2"|1=Decls BEGIN S END}},这里的<code>Decls</code>是一序列的声明,S是一个语句。块介入在<code>Decls</code>中声明的常量、类型、变量和过程,并接着执行<code>S</code>。声明的名字的作用域是这个块。 ====选择控制结构==== Modula-3提供了选择[[控制结构]]<code>IF</code>和<code>CASE</code>语句。<code>IF</code>语句有如下形式: <syntaxhighlight lang="modula2"> IF B_1 THEN S_1 ELSIF B_2 THEN S_2 ... ELSIF B_n THEN S_n ELSE S_0 END </syntaxhighlight> 这里的这些<code>B</code>是布尔表达式,而这些<code>S</code>是语句。<code>ELSE S_0</code>和每个<code>ELSIF B_i THEN S_i</code>都是可选的。<code>IF</code>语句依次求值<code>B</code>,直到某个<code>B_i</code>求值为<code>TRUE</code>,则接着执行<code>S_i</code>。如果没有表达式求值为<code>TRUE</code>,并且<code>ELSE S_0</code>存在,则执行<code>S_0</code>。如果没有表达式求值为<code>TRUE</code>,并且<code>ELSE S_0</code>缺席,则<code>IF</code>语句是无操作的(no-op)。Modula-3的<code>CASE</code>语句,不同于[[Modula-2]]的同名语句,它有如下形式: <syntaxhighlight lang="modula2"> CASE Expr OF L_1 => S_1 | ... | L_n => S_n ELSE S_0 END </syntaxhighlight> 这里的<code>Expr</code>是类型为序数类型的一个表达式,而每个<code>L</code>是一个列表,这个列表构成自常量表达式,或用<code>e_1..e_2</code>指示的常量表达式的范围,它表示从<code>e_1</code>到<code>e_2</code>含二者的值。如果<code>e_1</code>超出了<code>e_2</code>,这个范围为空。如果两个<code>L</code>表示的[[集合 (数学)|集合]]有交叠,或者任何常量表达式不是类型<code>Expr</code>的一个成员,则是一个静态错误。<code>ELSE S_0</code>是可选的。 <code>CASE</code>语句求值<code>Expr</code>。如果结果的值在任何<code>L_i</code>之中,则执行<code>S_i</code>。如果这个值不在任何<code>L_i</code>之中,并且<code>ELSE S_0</code>存在,则执行它。如果这个值不在任何<code>L_i</code>中,并且<code>ELSE S_0</code>缺席,则发生一个必查的运行时间错误。一些编程者使用竖杠作为初始符(initiator),另一些编程者将它用作分隔符。Modula-3允许这两种风格,语言报告将其用作分隔符。 ====分配类型测试==== <code>TYPECASE</code>语句有如下形式: <syntaxhighlight lang="modula2"> TYPECASE Expr OF T_1 (v_1) => S_1 | ... | T_n (v_n) => S_n ELSE S_0 END </syntaxhighlight> 这里的<code>Expr</code>是类型为引用类型的一个表达式,这些<code>S</code>是语句,这些<code>T</code>是引用类型,而这些<code>v</code>是标识符。如果<code>Expr</code>有类型<code>ADDRESS</code>,或者任何<code>T</code>不是<code>Expr</code>的类型的子类型,则为一个静态错误。<code>ELSE S_0</code>和每个<code>(v)</code>都是可选的。如果<code>(v_i)</code>缺席,形式<code>T_1, ..., T_n => S</code>,是<code>T_1 => S | ... | T_n => S</code>的简写。 <code>TYPECASE</code>语句求值<code>Expr</code>,如果结果的引用值,是任何列出的类型<code>T_i</code>的成员,则针对其中最小的<code>i</code>,执行<code>S_i</code>。<code>NIL</code>是所有引用类型的一个成员,因此<code>NULL</code>情况只有首先出现才能起作用。如果这个值不是列出的这些类型的一个成员,并且<code>ELSE S_0</code>存在,则执行它。如果这个值不是列出的这些类型的一个成员,并且<code>ELSE S_0</code>缺席,则发生一个必查的运行时间错误。每个<code>(v_i)</code>声明类型为<code>T_i</code>的一个变量,并且它的[[作用域]]是<code>S_i</code>。如果<code>v_i</code>存在,它在执行<code>S_i</code>之前被初始化为<code>Expr</code>的值。 可以使用<code>TYPECASE</code>语句测试类型为<code>REFANY</code>或对象的一个表达式的值的引用类型,比如写一个过程<code>ToText(r: REFANY): TEXT</code>,在其中测试<code>r</code>的值是<code>NULL</code>、<code>REF BOOLEAN</code>和<code>REF INTEGER</code>中的哪一个类型。 ====循环控制结构==== Modula-3提供了循环[[控制结构]]<code>FOR</code>、<code>LOOP</code>、<code>WHILE</code>和<code>REPEAT</code>语句,和必须在字面上包围在这四个循环语句中,用于从循环中退出的<code>EXIT</code>语句。语句{{code|2="modula2"|1=LOOP S END}},循环直到<code>S</code>中有一个{{code|2="modula2"|1=EXIT}}发生。语句{{code|2="modula2"|1=WHILE B DO S END}},被定义为: <syntaxhighlight lang="modula2"> LOOP IF B THEN S ELSE EXIT END END </syntaxhighlight> 语句{{code|2="modula2"|1=REPEAT S UNTIL B}},被定义为: <syntaxhighlight lang="modula2"> LOOP S; IF B THEN EXIT END END </syntaxhighlight> <code>FOR</code>语句有如下形式:{{code|2="modula2"|1=FOR id := first TO last BY step DO S END}},这里的<code>id</code>是一个标识符,而<code>first</code>和<code>last</code>是有相同基础类型的序数表达式,<code>step</code>是整数取值的表达式。而<code>S</code>是一个语句。<code>BY step</code>是可选的,如果省略,<code>step</code>缺省为<code>1</code>。标识符<code>id</code>指示其[[作用域]]是<code>S</code>的一个只读变量,其类型是<code>first</code>和<code>last</code>共同的基础类型。<code>FOR</code>语句的语义,可如下这样用<code>WHILE</code>和<code>WITH</code>语句精确的定义为一个块: <syntaxhighlight lang="modula2"> VAR i := ORD(first); done := ORD(last); delta := step; BEGIN IF delta >= 0 THEN WHILE i <= done DO WITH id = VAL(i, T) DO S END; INC(i, delta) END ELSE WHILE i >= done DO WITH id = VAL(i, T) DO S END; INC(i, delta) END END END </syntaxhighlight> 这里的<code>T</code>是<code>id</code>的类型,<code>i</code>、<code>done</code>和<code>delta</code>代表不出现在<code>FOR</code>语句中的变量。从<code>FOR</code>语句的这个定义可以看出,如果一个<code>FOR</code>循环的上界是<code>LAST(INTEGER)</code>或<code>LAST(LONGINT)</code>,就应当将它改写为<code>WHILE</code>循环来避免溢出。 ====With语句==== Modula-3的<code>WITH</code>语句,无关于[[Modula-2]]的同名语句,它有如下形式:{{code|2="modula2"|1=WITH id = e DO S END}},这里的<code>id</code>是一个标识符,<code>e</code>是一个表达式,而<code>S</code>是一个语句。<code>WITH</code>语句声明了具有[[作用域]]<code>S</code>的<code>id</code>,作为变量<code>e</code>的别名,或作为值<code>e</code>的只读名字。表达式<code>e</code>在进入<code>WITH</code>语句的时候被求值一次。一个单一的<code>WITH</code>语句可以包含多个绑定,它们顺序的求值,就是说{{code|2="modula2"|1=WITH id_1 = e_1, id_2 = e_2, ...}},等价于{{code|2="modula2"|1=WITH id_1 = e_1 DO WITH id_2 = e_2 DO ....}}。 <code>WITH</code>语句可类比于某种过程调用<code>P(e)</code>,这里的<code>P</code>被声明为: <syntaxhighlight lang="modula2"> PROCEDURE P(mode id: typeof e) = BEGIN S END P </syntaxhighlight> 这里的算符<code>[[typeof]]</code>只是个示意,它不在语言规定之中;如果<code>e</code>是可写的指定式,则<code>mode</code>是<code>VAR</code>,否则<code>mode</code>是<code>READONLY</code>。在<code>WITH</code>语句和调用<code>P(e)</code>之间唯一的区别为,出现在<code>WITH</code>语句内的[[自由变量]]、<code>RETURN</code>和<code>EXIT</code>,是在<code>WITH</code>语句所在的[[上下文 (计算机)|上下文]]中解释的,而非在新增加的<code>P</code>的上下文中。 Modula-3语言报告使用<code>WITH</code>语句定义其他语句,比如,<code>INC</code>和<code>DEC</code>语句分别有如下形式:{{code|2="modula2"|1=INC(v, n)}}和{{code|2="modula2"|1=DEC(v, n)}},这里的<code>v</code>指示一个[[序数]]类型的变量,而<code>n</code>是可选的一个[[整数]]取值表达式,如果省略<code>n</code>则缺省为<code>1</code>,这两个语句分别等价于{{code|2="modula2"|1=WITH x = v DO x := VAL(ORD(x) + n, T) END}}和{{code|2="modula2"|1=WITH x = v DO x := VAL(ORD(x) - n, T) END}},这里的<code>T</code>是<code>v</code>的类型,而<code>x</code>代表不出现在<code>n</code>中的变量。 ===例外=== 例外声明有如下形式:<code>EXCEPTION id(T)</code>,这里的<code>id</code>是一个标识符,而<code>T</code>是除了开放数组之外的一个类型,它声明了<code>id</code>是具有实际参数类型<code>T</code>的例外。如果省略了<code>(T)</code>,这个例外不接受实际参数。例外声明只允许出现在接口和模块的最外层作用域之中。所有的声明的例外都是各不相同的。 <code>RAISE</code>语句有两种形式,没有实际参数的形式:{{code|2="objectpascal"|1=RAISE e}},这里的<code>e</code>是不接受实际参数的一个例外,它的结果是发起例外<code>e</code>;具有实际参数的形式:{{code|2="objectpascal"|1=RAISE e(x)}},这里的<code>e</code>是接受实际参数的一个例外,而<code>x</code>是可赋值给<code>e</code>的实际参数类型的一个表达式,它的结果是引发具有配对实际参数<code>x</code>的例外<code>e</code>。 Modula-3的[[例外处理 (编程)|例外处理]],基于了<code>TRY-EXCEPT</code>语句。采用类似的块系统的有[[Delphi]]、[[Python]]<ref>{{cite web|url= https://www.python.org/doc/faq/general/#why-was-python-created-in-the-first-place|title= Why was Python created in the first place?|quote= I had some experience with using Modula-2+ and talked with the designers of Modula-3 and read the Modula-3 report. Modula-3 is the origin of the syntax and semantics used for exceptions, and some other Python features.|access-date= 2021-06-22|archive-date= 2008-02-23|archive-url= https://web.archive.org/web/20080223222507/http://www.python.org/doc/faq/general/#why-was-python-created-in-the-first-place}}</ref>、[[Scala]]<ref>{{Cite web |url=http://scala.epfl.ch/ |title=Scala Center at EPFL. |access-date=2022-05-15 |archive-date=2020-09-23 |archive-url=https://web.archive.org/web/20200923124042/https://scala.epfl.ch/ }}</ref>和[[Visual Basic.NET]]。 <code>TRY-EXCEPT</code>语句有如下形式: <syntaxhighlight lang="objectpascal"> TRY Body EXCEPT id_1 (v_1) => Handler_1 | ... | id_n (v_n) => Handler_n ELSE Handler_0 END </syntaxhighlight> 这里的<code>Body</code>和每个<code>Handler</code>都是语句,每个<code>id</code>指名一个例外,而每个<code>v_i</code>是一个标识符。<code>ELSE Handler_0</code>和每个<code>(v_i)</code>都是可选的。每个<code>(v_i)</code>声明一个变量,其类型是例外<code>id_i</code>的实际参数类型,而它的[[作用域]]是<code>Handler_i</code>。如果<code>(v_i)</code>缺席,形式<code>id_1, ..., id_n => Handler</code>,是<code>id_1 => Handler; ...; id_n => Handler</code>的简写。 <code>TRY</code>子句执行<code>Body</code>。如果结果是正常的,则忽略<code>EXCEPT</code>等子句。如果<code>Body</code>引发任何列出的例外<code>id_i</code>,则执行<code>Handler_i</code>。在处理具有配对实际参数<code>x</code>的例外<code>id_i</code>的时候,<code>v_i</code>在执行<code>Handler_i</code>之前被初始化为<code>x</code>。如果<code>Body</code>引发任何其他例外,并且<code>ELSE Handler_0</code>存在,则执行它。在这两种情况的任何之一下,<code>TRY-EXCEPT</code>语句的结果是选出的处理器的结果。如果<code>Body</code>引发未列出的例外,而<code>ELSE Handler_0</code>缺席,则<code>TRY-EXCEPT</code>语句的结果是<code>Body</code>引发的例外。 Modula-3还提供<code>TRY-FINALLY</code>语句,它有如下形式:{{code|2="objectpascal"|1=TRY S_1 FINALLY S_2 END}},它执行语句<code>S_1</code>接着执行语句<code>S_2</code>。如果<code>S_1</code>的结果是正常的,则<code>TRY</code>语句的结果等价于<code>S_1; S_2</code>。如果<code>S_1</code>的结果是例外,并且<code>S_2</code>的结果是正常,则在<code>S_2</code>执行之后,重新发起来自<code>S_1</code>的例外。如果二者的结果都是例外,则<code>TRY</code>语句的结果是来自<code>S_2</code>的例外。 在Modula-3中,{{code|2="modula2"|1=EXIT}}和{{code|2="modula2"|1=RETURN}}语句的语义,分别被定义为发起叫做<code>exit-exception</code>和<code>return-exception</code>的例外。<code>exit-exception</code>不接受实际参数,<code>return-exception</code>接受任意类型的实际参数,程序不能显式的命名这些例外。语句{{code|2="modula2"|1=LOOP S END}},非正式的等价于: <syntaxhighlight lang="objectpascal"> TRY S; S; S; ... EXCEPT exit-exception => (*skip*) END </syntaxhighlight> ===过程=== ====过程类型==== 一个[[子程序|过程]]要么是<code>[[空指针|NIL]]</code>,要么是有如下构成的[[三元组]]: *过程体,它是一个语句。 *[[类型签名|签名]],它有如下形式<code>(formal_1; ...; formal_n): R RAISES S</code>,这里的每个<code>formal_i</code>是形式参数,<code>R</code>是结果类型,<code>S</code>是<code>RAISES</code>集合(这个过程可以引发的例外的集合)。 *环境,它是在解释过程体中变量名字方面有关的[[作用域]]。 形式参数有如下形式:{{code|2="objectpascal"|1=Mode Name: Type := Default}},这里的: *<code>Mode</code>是参数模态,它可以是<code>VALUE</code>、<code>VAR</code>或<code>READONLY</code>。如果省略了<code>Mode</code>,它缺省为<code>VALUE</code>。 *<code>Name</code>是命名这个形式参数的标识符。形式参数名字必须是各不相同的。 *<code>Type</code>是这个形式参数的类型。 *<code>Default</code>是一个常量表达式,它是这个形式参数的缺省值。如果<code>Mode</code>是<code>VAR</code>,则<code>:= Default</code>必须省略,否则<code>:= Default</code>和<code>: Type</code>中任何一个都可以省略,但不能二者都省略。如果省略了<code>Type</code>,它采用<code>Default</code>的类型。如果二者都存在,<code>Default</code>的值必须是<code>Type</code>的一个成员。 形式{{code|2="objectpascal"|1=Mode v_1, ..., v_n: Type := Default}},是{{code|2="objectpascal"|1=Mode v_1: Type := Default; ...; Mode v_n: Type := Default}}的简写。 <code>VAR</code>形式参数绑定到对应的实际参数所指示的变量上,也就是说它是别名。对于<code>VAR</code>形式参数,实际参数必须是可写的指定式,它的类型同于形式参数的类型,或者在<code>VAR</code>数组形式参数的情况下,它可赋值给这个形式参数。 <code>VALUE</code>形式参数绑定到具有未用的位置的一个变量上,并且它被初始化为实际参数对应的值。对于<code>VALUE</code>或<code>READONLY</code>形式参数,实际参数是可赋值给形式参数类型的任何表达式,并且放开了针对赋值局部过程的禁止。 <code>READONLY</code>形式参数,如果实际参数是指定式并且有与形式参数相同的类型,或者是可赋值给形式参数的类型的一个数组类型,则被当作<code>VAR</code>形式参数对待,它的只读语义体现在指定这个指定式为只读的,否则被当作<code>VALUE</code>形式参数对待。 在签名中,如果省略了<code>: R</code>,则这个过程是真正(proper)过程,否则为函数过程;如果省略了<code>RAISES S</code>,则假定它为<code>RAISES {}</code>。一个过程发起未包括在<code>RAISES</code>集合中的一个例外,是一个必查的运行时间错误,如果语言实现将这个运行时间错误映射成一个例外,则这个例外隐含的包括在所有的<code>RAISES</code>子句中。 ====过程声明==== 有两种形式的过程声明:只允许出现在接口中的{{code|2="modula2"|1=PROCEDURE id sig}},和只允许出现在模块中的{{code|2="modula2"|1=PROCEDURE id sig = B id}},这里的<code>id</code>是一个标识符,<code>sig</code>是过程[[类型签名|签名]],而<code>B</code>是一个块。在一个模块的最外层作用域内声明的过程是顶层过程,其他过程是局部过程。局部过程可以作为形式参数传递但不能被赋值,因为在栈实现下,局部过程在包含它的[[调用栈|栈桢]]被弹出后会成为无效的。 过程类型声明有如下形式:{{code|2="modula2"|1=TYPE T = PROCEDURE sig}},这里的<code>sig</code>是[[类型签名|签名]]规定。一个过程<code>P</code>是过程类型<code>T</code>的一个成员,或称为是它的一个值的条件为,它是<code>NIL</code>,或者它的签名被<code>T</code>的签名所含盖(cover)。这里的签名<code>sig_A</code>含盖签名<code>sig_B</code>的条件是: *它们有相同数目的形式参数,并且对应的形式参数有相同的类型和模态。 *它们有相同的结果类型,或者都没有结果类型。 *<code>sig_A</code>的<code>RAISES</code>集合包含<code>sig_B</code>的<code>RAISES</code>集合。 过程常量是声明为过程的一个标识符。过程变量是声明为具有过程类型的一个变量。例如: <syntaxhighlight lang="modula2"> PROCEDURE P(txt: TEXT := "P") = BEGIN IO.Put(txt) END P; VAR q: PROCEDURE(txt: TEXT := "Q") := P; </syntaxhighlight> 声明了过程常量<code>P</code>和过程变量<code>q</code>。两个过程如果有一致的[[闭包 (计算机科学)|闭包]],就是说它们涉及相同的过程体和环境,则二者相等即有相同的值。形式参数名字和缺省值,影响一个过程的类型即签名,而非这个过程的值。将一个过程常量赋值给一个过程变量,改变这个它的值,而非它的类型。比如上例中有:<code>P = q</code>为<code>TRUE</code>。缺省的形式参数的解释,由一个过程的类型来确定,而非这个过程的值。比如上例子中:<code>P()</code>打印<code>P</code>,而<code>q()</code>打印<code>Q</code>。 ====过程调用==== 过程调用有如下形式:<code>P(Bindings)</code>,这里的<code>P</code>是一个过程取值的表达式,而<code>Bindings</code>是关键字或位置绑定的一个列表。关键字绑定有如下形式:<code>name := actual</code>,这里的<code>actual</code>是一个表达式,而<code>name</code>是一个标识符。位置绑定有如下形式:<code>actual</code>,这里的<code>actual</code>是一个表达式。当关键字和位置绑定混合在一个调用中的时候,位置绑定必须前导于关键字绑定。如果绑定列表为空,圆括号仍然是需要的。 绑定列表要如下这样重写,来适合<code>P</code>的类型签名:首先,每个位置绑定<code>actual</code>,若为<code>Bindings</code>中的第<code>i</code>个绑定,则通过补充上第<code>i</code>个形式参数的名字,转换成关键字绑定增加到关键字绑定列表中。其次,对于有缺省值并且在第一步之后没有被绑定的每个形式参数,将它的形式参数的名字<code>name</code>和它的缺省值<code>default</code>,形成关键字绑定<code>name := default</code>增加到关键字绑定列表。重写成的关键字绑定列表必须只绑定形式参数,并且必须只绑定每个形式参数正好一次。 执行过程调用,需要求值过程取值表达式<code>P</code>和它的实际参数,绑定形式参数,并执行过程体。<code>P</code>和它的实际参数的求值次序是未定义的。调用一个未定义或<code>NIL</code>过程,是一个必查的运行时间错误。调用具有过程体<code>B</code>的真正过程,在绑定了实际参数之后等价于: <syntaxhighlight lang="objectpascal"> TRY B EXCEPT return-exception => (*skip*) END </syntaxhighlight>而调用具有过程体<code>B</code>的函数过程等价于: <syntaxhighlight lang="objectpascal"> TRY B; (错误:没有返回值) EXCEPT return-exception (v) => (结果成为v) END </syntaxhighlight> ===递归定义=== 对于常量、类型或过程声明<code>N = E</code>、变量声明<code>N: E</code>、例外声明<code>N(E)</code>或揭示<code>N = E</code>,如果<code>N</code>出现在<code>E</code>的任何部份展开之中,则它是[[递归 (计算机科学)|递归]]的。对于省略了类型的变量声明<code>N := I</code>,如果<code>N</code>出现在<code>I</code>的类型<code>E</code>的任何部份展开中,则它是[[递归 (计算机科学)|递归]]的。允许这种声明的条件,是<code>N</code>在<code>E</code>的任何部份展开中所有的出现为如下三者之一:在[[类型构造子]]<code>REF</code>或<code>PROCEDURE</code>的某个出现之内,在[[类型构造子]]<code>OBJECT</code>的一个字段或方法类型之内,或者在一个过程体之内。下面是合法的递归声明的例子: <syntaxhighlight lang="objectpascal"> 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; </syntaxhighlight> ==特征性技术== Modula-3的设计者认为[[Modula-2]]最成功的特征,是提供了在[[模块化编程|模块]]之间的显式[[介面 (資訊科技)|接口]],故而接口在Modula-3中保留而在本质上没有变化。到模块的接口,是披露了模块公开部份的声明[[集合 (计算机科学)|搜集]],而未声明于接口中的模块中的东西是私有的。模块导入它所依赖的接口,而导出它所实现的接口(在Modula-3中可以是多个接口)。 在现代编程语言的实现下,一个抽象类型的值,被表示一个[[对象 (计算机科学)|对象]],它的操作由叫做对象的[[方法 (计算机科学)|方法]]的过程值的一个套件来实现。一个新的对象类型,可以定义为现存类型的[[子类型]],在这种情况下新类型拥有旧类型的所有方法,并且可能还有新的方法([[继承 (计算机科学)|继承]])。新类型可以为旧方法提供新的实现([[方法覆盖|覆盖]])。对象类型和[[Modula-2]]的[[不透明数据类型|不透明类型]]的组合,产生了一个新事物:部份不透明(partially opaque)类型,这里的一个对象的一些字段在一个作用域内是可见的,而其他的是隐藏的。 [[泛型编程|泛型]]模块是一种[[模板元编程|模板]],在其中一些导入的接口被当作形式参数,它们在[[实例 (计算机科学)|实例化]]泛型的时候要绑定到实际接口。不同的泛型实例是独立编译的,源代码程序被[[代码复用|重复使用]],而编译后的代码对不同实例通常是不同的。为保持简单性,在Modula-3中泛型被限制在模块层面,泛型的过程和类型不孤立存在,并且[[参数多态|泛型形式参数]]必须都是完全的接口。 将计算分解入[[并发性|并发]][[进程]](或[[线程]])中是[[关注点分离]]的基本方法。[[Modula-2]]提供的线程很弱,本质上相当于[[协程]]。Modula-3采用了[[東尼·霍爾|霍尔]]的[[監視器 (程序同步化)|监视器]],它是[[并发计算|并发编程]]的更坚实基础。{{le|Modula-2+}}在设计中加入了简化的[[Mesa语言|Mesa]]同步原语,它足够成功而在实质上没有改变的结合入了Modula-3。 一个语言特征是不安全的,如果它的滥用可以破坏运行时系统,使得程序的进一步运行不忠实于语言的语义,不安全特征的一个例子,是没有[[边界检查]]的数组赋值。一个安全的程序的一个错误,可以导致计算中止,并给出运行时间错误信息或给出错误解答,但它不会导致计算分崩离析。Modula-3在这个领域里跟从Cedar,接受只在显式的标记为不安全的模块中才允许的少量不安全特征。 ===模块化=== [[介面 (資訊科技)|接口]]中的声明,除了任何变量初始化必须是常量,和过程声明必须只能指定签名而不能有过程体之外,同于在[[块 (编程)|块]]中的声明。[[介面 (資訊科技)|接口]]有如下形式: <syntaxhighlight lang="objectpascal"> INTERFACE id; Imports; Decls END id. </syntaxhighlight> 这里的<code>id</code>是命名这个接口的标识符,<code>Imports</code>是一序列的导入语句,而<code>Decls</code>是一序列的声明。在<code>Decls</code>中的名字和可见的导入的名字必须是各不相同的。两个或更多个接口形成导入环是静态错误。典型的接口定义,通常为每个接口定义一个数据结构(记录或对象)以及任何的支持过程。 [[模块化编程|模块]]除了实体名字的可见性之外,就像是一个[[块 (编程)|块]]。一个实体在一个块中是可见的,如果它被声明在这个块中,或在某个包围的块中;而一个实体在一个模块中是可见的,如果它被声明在这个模块中,或在这个模块所导入或导出的一个接口中。[[模块化编程|模块]]有如下形式: <syntaxhighlight lang="modula2"> MODULE id EXPORTS Interfaces; Imports; Block id. </syntaxhighlight> 这里的<code>id</code>是命名这个模块的一个标识符,<code>Interfaces</code>是这个模块导出的那些接口的不同名字的一个列表,<code>Imports</code>是一序列的导入语句,而<code>Block</code>是一个块,它是模块体。名字<code>id</code>必须重复于终结模块体的<code>END</code>之后。<code>EXPORTS Interfaces</code>可以省略,在这种情况下<code>Interfaces</code>缺省为<code>id</code>,即模块将缺省导出相同名称的接口。Modula-3中的导出<code>EXPORTS</code>,与[[Modula-2]]中的导出<code>EXPORT</code>无关,它在概念上对应于Modula-2的“实现”。 如果模块<code>M</code>导出了接口<code>I</code>,则在<code>I</code>中声明的所有名字在<code>M</code>中是不加以限定而可见的。一个模块<code>M</code>导出一个接口<code>I</code>,可以重新声明在这个接口中声明的一个或多个过程,为其提供过程体。在<code>M</code>中的[[类型签名|签名]],必须被在<code>I</code>中的签名所含盖。要确定在对这个过程的调用中的关键字绑定的解释,在<code>M</code>内使用<code>M</code>中的签名,在其他地方使用<code>I</code>中的签名。除了重新声明导出的过程之外,在<code>Block</code>的顶层中声明的名字,可见的导入的名字,和在导出的接口中声明的名字必须是各不相同的。 一个程序的模块和接口的名字叫做全局名字。查找全局名字的方法是依赖于实现的,比如通过[[文件系统]][[PATH (变量)|查找]][[路径 (计算机科学)|路径]]。一个模块或接口<code>X</code>导入一个接口<code>I</code>,从而使得在<code>I</code>中声明的实体和揭示在<code>X</code>中可见,但不包括<code>I</code>所导入的那些。一个模块<code>M</code>使用了接口<code>I</code>,如果<code>M</code>导入或导出了<code>I</code>,或者<code>M</code>使用了导入<code>I</code>的一个接口。Modula-3中所有的编译单元,要么是接口要么是模块。任何编译单元都可以使用<code>IMPORT</code>语句从其他接口导入标识符。与其他语言的[[包含指令|包含]]功能不同,任何标识符都可以追踪到它的起源位置。 <code>IMPORT</code>语句有如下的典型使用形式:{{code|2="modula2"|1=IMPORT I}},它导入全局名字为<code>I</code>的接口,并给它一个同样的局部名字,从而使用[[点表示法]]访问接口中的实体(类似于访问记录中的字段)。比如接口中的主要类型按惯例命名为<code>T</code>,则使用它时采用的限定形式为<code>I.T</code>。如果导入接口的名字与模块内的其他实体的名字发生冲突,则可以使用{{code|2="modula2"|1=IMPORT I AS X}}这样的形式给与它不同的局部名字。 <code>IMPORT</code>语句还有如下的另一种形式:{{code|2="modula2"|1=FROM I IMPORT N}},它为在接口<code>I</code>中声明为<code>N</code>的实体,介入一个局部名字<code>N</code>,比如:{{code|2="modula2"|1=FROM IO IMPORT Put}},并且不加限定的使用它,比如:{{code|2="modula2"|1=Put("Hello World\n")}}。局部绑定优先于全局绑定,例如:{{code|2="modula2"|1=IMPORT I AS J, J AS I; FROM I IMPORT N}},为全局名字为<code>J.N</code>的实体介入了局部名字<code>N</code>。非法使用相同的局部名字两次是静态错误。 一个程序是模块和接口的搜集,包含了任何它的模块或接口所导入或导出的所有接口,并且在其中没有重复定义的过程、模块或接口。执行一个程序的效果是执行它的所有模块的模块体。模块的执行次序受到初始化规则的约束:如果模块<code>M</code>依赖于模块<code>N</code>并且<code>N</code>不依赖于<code>M</code>,则<code>N</code>的模块体将先于<code>M</code>的模块体执行。这里的模块<code>M</code>依赖于模块<code>N</code>,如果M使用了<code>N</code>导出的一个接口,或者<code>M</code>所依赖的一个模块依赖于<code>N</code>。 其模块体最后执行的模块叫做主模块。在初始化规则不能唯一的确定它的情况下,语言实现提供了指定这个主模块的方法,即主模块是导出<code>Main</code>的模块,它的内容是依赖于实现的。程序执行在主模块终止的时候终止,即使并发的控制线程仍在执行。 下面是接口与模块的一个规范性的例子,是有隐藏表示的一个公开[[堆栈]]: <syntaxhighlight lang="objectpascal"> INTERFACE Stack; TYPE T <: REFANY; PROCEDURE Create(): T; PROCEDURE Push(VAR s: T; x: REAL); PROCEDURE Pop(VAR s: T): REAL; END Stack. </syntaxhighlight> <syntaxhighlight lang="modula2"> 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. </syntaxhighlight> 如果多于一个模块需要此堆栈的表示,则应当把它转移到私有接口中,从而在需要它的任何地方的都可以导入它: <syntaxhighlight lang="objectpascal"> INTERFACE Stack; (* ... 同上 ... *) END Stack. INTERFACE StackRep; IMPORT Stack; REVEAL Stack.T = BRANDED OBJECT item: REAL; link: Stack.T END END StackRep. </syntaxhighlight> <syntaxhighlight lang="modula2"> MODULE Stack; IMPORT StackRep; (* Push、Pop和Create同上 *) BEGIN END Stack. </syntaxhighlight> ===对象类型=== 一个[[对象 (计算机科学)|对象]]要么是<code>[[空指针|NIL]]</code>,要么是到有配对的[[方法 (计算机科学)|方法]]套件的一个数据[[记录]]的[[参照|引用]],方法套件是接受这个对象作为第一个实际参数的那些[[子程序|过程]]的一个记录。这个数据记录可以包含经由对象类型的[[子类型]]介入的增加字段,这个套件可以包含经由子类型介入的增加方法。对象[[赋值语句|赋值]]是引用赋值。要复制一个对象的数据记录到另一个对象,这些字段必须单独的赋值。 Modula-3意图保持采用[[对象 (计算机科学)|对象]]这个最简单的术语,而非在其他面向对象语言中对应的术语[[类 (计算机科学)|类]]。对象类型声明采用如下形式: <syntaxhighlight lang="objectpascal"> TYPE T = ST OBJECT Fields METHODS Methods OVERRIDES Overrides END </syntaxhighlight> 这里的<code>ST</code>是可选的[[子类型|超类型]],如果省略则缺省为<code>ROOT</code>,<code>Fields</code>是完全如同记录类型那样的字段声明的一个列表,<code>Methods</code>是方法声明的一个列表,而<code>Overrides</code>是方法覆盖的一个列表。在字段和方法声明中介入的名字,必须相互不同,并且不同于在覆盖声明中的名字。<code>T</code>的字段构成自<code>ST</code>的字段和随后的新声明的字段。<code>T</code>的方法构成自经过<code>OVERRIDES</code>中的覆盖修改的<code>ST</code>的方法,和随后的<code>METHODS</code>中声明的方法。<code>T</code>有着同<code>ST</code>一样的引用类。 关键字<code>OBJECT</code>可选的可以前导上<code>BRANDED</code>或<code>BRANDED b</code>来给对象加铭牌,使之唯一而避免结构等价,这里的<code>b</code>是文本常量。其含义同于非对象的引用类型,<code>b</code>被省略时,系统自动生成唯一性的一个字符串。 方法声明有如下形式:<code>m sig := proc</code>,这里的<code>m</code>是个标识符,<code>sig</code>是过程[[类型签名|签名]],而<code>proc</code>是顶层过程常量。方法声明指定了<code>T</code>的方法<code>m</code>有签名<code>sig</code>和值<code>proc</code>。如果省略了<code>:= proc</code>假定它为<code>:= NIL</code>。如果<code>proc</code>非空,它的第一个形式参数必须是缺省的模态<code>VALUE</code>,并有着<code>T</code>的某个超类型的类型(包括且经常是<code>T</code>自身),并且去除了第一个形式参数结果就是<code>sig</code>所含盖的签名。 方法[[方法覆盖|覆盖]](override)有如下形式:<code>m := proc</code>,这里的<code>m</code>是超类型<code>ST</code>的方法的名字,而<code>proc</code>是顶层过程常量。方法覆盖指定了<code>T</code>的方法<code>m</code>是<code>proc</code>,并非指定<code>ST.m</code>。如果<code>proc</code>非空,它的第一个形式参数必须是缺省的模态<code>VALUE</code>,并有着<code>T</code>的某个超类型的类型(包括且经常是<code>T</code>自身),去除了<code>proc</code>第一个形式参数结果就是<code>ST</code>的<code>m</code>方法所含盖的签名。举例说明: <syntaxhighlight lang="objectpascal"> TYPE Super = OBJECT a: INTEGER; METHODS p() END; Sub = Super OBJECT b: INTEGER END; PROCEDURE ProcSuper(self: Super) = ... ; PROCEDURE ProcSub(self: Sub) = ... ; </syntaxhighlight> 这里的过程<code>ProcSuper</code>和<code>ProcSub</code>是类型<code>Super</code>和<code>Sub</code>的对象的<code>p</code>方法的候选值。例如: {| class = wikitable |- style="vertical-align:top" | <syntaxhighlight lang="objectpascal"> TYPE T1 = Sub OBJECT OVERRIDES p := ProcSub END </syntaxhighlight> || <syntaxhighlight lang="objectpascal"> TYPE T2 = Super OBJECT OVERRIDES p := ProcSuper END </syntaxhighlight> |- | 声明了具有<code>Sub</code>数据记录和预期一个<code>Sub</code>的<code>p</code>方法的一个类型。<code>T1</code>是<code>Sub</code>的有效的子类型。 || 声明了具有<code>Super</code>数据记录和预期一个<code>Super</code>的<code>p</code>方法的一个类型。<code>T2</code>是<code>Super</code>的有效的子类型。 |- style="vertical-align:top" | <syntaxhighlight lang="objectpascal"> TYPE T3 = Sub OBJECT OVERRIDES p := ProcSuper END </syntaxhighlight> || <syntaxhighlight lang="objectpascal"> TYPE T4 = Super OBJECT OVERRIDES p := ProcSub END </syntaxhighlight> |- | 声明了具有<code>Sub</code>数据记录和预期一个<code>Super</code>的<code>p</code>方法的一个类型。因为所有<code>Sub</code>都是<code>Super</code>,这个方法不挑剔要放入的对象。 || 尝试声明具有<code>Super</code>数据记录和预期一个<code>Sub</code>的<code>p</code>方法的一个类型,因为不是所有<code>Super</code>都是<code>Sub</code>,这个方法很挑剔要放入的对象。<code>T4</code>的声明是个静态错误。 |} 如果<code>T</code>是对象类型或到记录的引用,<code>NEW(T)</code>操作有如下形式:{{code|2="modula2"|1=NEW(T, Bindings)}},这里的<code>Bindings</code>是用来初始化新字段的关键字绑定的一个列表,不允许位置绑定。每个绑定<code>f := v</code>将字段<code>f</code>初始化为值<code>v</code>。如果<code>T</code>是对象类型,则<code>Bindings</code>还可以包括形如<code>m := P</code>的方法覆盖,这里的<code>m</code>是<code>T</code>的一个方法而<code>P</code>是一个顶层过程常量。{{code|2="modula2"|1=NEW(T, m := P)}}是{{code|2="objectpascal"|1=NEW(T OBJECT OVERRIDES m := P END)}}的[[语法糖]]。 如果<code>o</code>是一个对象,则<code>o.f</code>指示在<code>o</code>的数据记录中名为<code>f</code>的数据字段。<code>o.f</code>是可写的指定式,它的类型是这个字段的所声明的类型。如果<code>m</code>是<code>o</code>方法之一,以<code>o.m(Bindings)</code>形式的过程调用,指示执行<code>o</code>的<code>m</code>方法,这等价于:<code>(o的m方法) (o, Bindings)</code>。 如果<code>T</code>是对象类型而<code>m</code>是<code>T</code>的方法的名字之一,则<code>T.m</code>指示<code>T</code>的<code>m</code>方法。它的过程类型的第一个实际参数有类型<code>T</code>,而第一个实际参数对应的形式参数名字是未指定的,因此在调用<code>T.m</code>时,第一个实际参数必须按位置来指定,不能用关键字。这种表示法便于子类型方法调用它的某个超类型的对应方法。 在子类型中的字段或方法遮盖(mask)了在超类型中的任何同名的字段或方法。要访问被遮盖的字段,使用类型运算<code>NARROW(x, T)</code>来将子类型变量当作超类型的成员,这里的<code>T</code>必须是对象类型或有跟踪的引用类型,而<code>x</code>必须可赋值给<code>T</code>。<code>NARROW(x, T): T</code>在检查了<code>x</code>是<code>T</code>一个成员之后返回<code>x</code>,如果检查失败则发生运行时间错误。 下面的例子展示了在声明新方法和覆盖现存方法之间的不同,首先声明如下: <syntaxhighlight lang="objectpascal"> 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); </syntaxhighlight> 然后可以用<code>a.m()</code>激活<code>ProcSuper(a)</code>,用<code>b.m()</code>激活<code>ProcSub(b)</code>,用<code>c.m()</code>激活<code>ProcSub(c)</code>,如此调用在覆盖和扩展之间没有区别。但是扩展的<code>c</code>的方法套件有两个方法,而覆盖的<code>b</code>的方法套件只有一个方法,这可以通过使用<code>NARROW(x, T)</code>将子类型的变量<code>b</code>和<code>c</code>视为超类型<code>Super</code>的成员来披露:<code>NARROW(b, Super).m()</code>激活<code>ProcSub(b)</code>,<code>NARROW(c, Super).m()</code>激活<code>ProcSuper(c)</code>。 对象不能{{le|解引用算符|Dereference operator|解引用}},因为在语言实现中,一个对象变量的静态类型不确定它的数据记录的类型。对象类型确定了关乎数据[[记录]][[字段]]和[[方法 (计算机科学)|方法]]套件的超类型链的前缀的那些类型。 ====实现==== 下面是语言设计者提出的对象的一种可能实现的梗概<ref name="type">{{cite web|url=http://lucacardelli.name/papers/modula3typesystem.a4.pdf|title=The Modula-3 Type System|authors=Luca Cardelli, Jim Donahue, Mick Jordan, Bill Kalsow, Greg Nelson|year=1989|access-date=2021-08-12|archive-date=2021-03-24|archive-url=https://web.archive.org/web/20210324050223/http://lucacardelli.name/Papers/Modula3TypeSystem.A4.pdf}}</ref>,一个对象可以表示为它的数据记录的第一个[[字 (计算机)|字]]的地址。前面的字存储一个对象头部,它包含一个唯一于对象类型的类型代码。这些类型代码是小型整数,对每个对象类型和每个有跟踪的引用类型都有一个代码。在对象头部之前的字,存储到这个对象的方法套件的一个引用。如果这个对象没有方法,这个字可以省略。这还允许对象共享方法套件,这是常见情况。如果<code>o</code>是一个对象,<code>d</code>是它的数据字段之一,而<code>m</code>是它的方法之一,则在这种表示下: {| class="wikitable" |- |<code>o.d</code> || 是 || <code>Mem[o + d]</code> |- |<code>o.m</code> || 是 || <code>Mem[Mem[o – 2] + m]</code> |- |<code>TYPECODE(o)</code> || 是 || <code>Mem[o – 1]</code> |} 这里假定了字段和方法以自然方式表示为[[偏移量]]。进一步的问题是如何高效的测试一个对象<code>o</code>是否有着类型<code>T</code>,这是<code>NARROW()</code>和<code>TYPECASE</code>语句所需要的。<code>NARROW()</code>的最简单实现,是维护一个以类型代码为索引的数组<code>st</code>,<code>st[tc]</code>是其类型代码为<code>tc</code>的对象类型的超类型的类型代码,如果它没有超类型则为<code>NIL</code>。要测试<code>o</code>是否是一个<code>T</code>,使用一个循环来计算<code>T</code>的类型代码是否出现在如下序列之中: <syntaxhighlight lang="modula2"> TYPECODE(o), st[TYPECODE(o)], st[st[TYPECODE(o)]], ... NIL </syntaxhighlight> 这个序列可以称为<code>o</code>的类型的超类型[[路径 (计算机科学)|路径]],而它的长度可称为<code>o</code>的类型的深度。利用上每个类型的深度都是编译时间确定的,因此可以同相应的类型代码一起存储的事实,可以有更快速的<code>NARROW()</code>实现。如果<code>T</code>的类型代码,出现在类型<code>U</code>的超类型路径上,它就该在位置<code>depth(U) - depth(T)</code>上。如果每个类型的超类型路径都是顺序[[数组]],这意味着<code>NARROW()</code>可以用恒定时间实现。因为超类型路径通常不太长,这是一个有吸引力的策略。在不常见的对象类型有非常长的超类型链的情况下,只有这个链的直到某个极大长度的一个前缀,会被顺序的存储。在运行时间,这个深度差如果超出这个链的顺序存储的长度,实现必须回退到[[链表]]。 ===不透明类型及其披露=== [[不透明数据类型|不透明]](opaque)类型声明有如下形式:{{code|2="modula2"|1=TYPE T <: U}},这里的<code>T</code>是标识符,而<code>U</code>是指示一个引用类型的表达式。它将名字<code>T</code>介入为不透明类型,并披露了<code>U</code>是<code>T</code>的超类型。 Modula-3的揭示(revelation)机制,将关于一个不透明类型的信息,介入到一个[[作用域]]之内,不同于其他声明,揭示不介入新的名字。不同的作用域,可以披露(reveal)关于一个不透明类型的不同的信息。例如,某个不透明类型,在一个作用域内知晓为<code>REFANY</code>的子类型,在其他的作用域内可以知晓为<code>ROOT</code>的子类型。 不透明类型名字所指示的实际类型叫做具体类型。<code>T</code>的具体类型,必须披露于程序的其他地方。在对象声明中,如果<code>ST</code>被声明为不透明类型,则只在知晓<code>ST</code>的具体类型为对象类型的作用域内,<code>T</code>的声明是合法的。如果<code>T</code>被声明为不透明类型,则只在完全知晓<code>T</code>的具体类型,或者知晓它为对象类型的作用域内,<code>NEW(T)</code>是合法的。 揭示分为两种:部份的和完全的,一个程序可以包含一个不透明类型的任意多个部份揭示,但必须包含唯一的一个完全揭示。 部份揭示有如下形式:{{code|2="modula2"|1=REVEAL T <: V}},这里的<code>V</code>是类型表达式(可能就是个名字),而<code>T</code>是被声明为不透明类型的一个标识符(可能有限定)。它披露了<code>V</code>是<code>T</code>的超类型。在任何作用域内,对一个不透明类型披露的超类型,必须是在子类型关系下是线性有序的。就是说,如果披露了<code>T <: U1</code>和<code>T <: U2</code>,则必须也要披露要么<code>U1 <: U2</code>要么<code>U2 <: U1</code>。{{code|2="modula2"|1=REVEAL T <: T}}是合法的非递归声明。 完全揭示有如下形式:{{code|2="modula2"|1=REVEAL T = V}},这里的<code>V</code>是类型表达式(不能就是个名字),它的最外层[[类型构造子]],是有铭牌的一个引用或对象类型,而<code>T</code>是被声明为不透明类型的一个标识符(可能有限定)。这个揭示指定了<code>V</code>是<code>T</code>的具体类型。在任何作用域内,披露为<code>T</code>的超类型的任何类型,都必须是<code>V</code>的超类型,否则是一个静态错误。不同的不透明类型有不同的具体类型,因为<code>V</code>包含了一个铭牌并且在程序中所有的铭牌都是独一的。{{code|2="modula2"|1=REVEAL I.T = I.T BRANDED OBJECT ... END}}是非法的递归声明。 揭示只允许用在接口和模块的最外层[[作用域]]内。在接口中的揭示可以被导入到任何需要它的作用域之内。揭示提供了向客户隐藏实现细节的,在概念上简单清晰却非常强力的机制,例如: <syntaxhighlight lang="objectpascal"> INTERFACE I; TYPE T <: 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 <: MUTEX; END IClass. </syntaxhighlight> 导入<code>I</code>的编译单元见到的<code>I.T</code>是<code>ROOT</code>的不透明子类型,因而被限制为可以分配类型<code>I.T</code>的对象,把它们传递给<code>I.P</code>,或声明<code>I.T</code>的子类型。导入<code>IRep</code>的编译单元见到的<code>I.T</code>是具体类型,它是有扩展的<code>count</code>字段的<code>MUTEX</code>的子类型。导入<code>IClass</code>的编译单元见到的<code>I.T</code>是<code>MUTEX</code>的不透明子类型,因而可以锁定类型<code>I.T</code>的对象。 ===泛型=== 在[[泛型编程|泛型]]接口或模块中,某些导入的接口名字被当作形式参数,它们在泛型被实例化的时候要绑定到实际接口上。泛型接口及其实例有如下左侧的形式,并等价于右侧定义的普通接口: {| class = wikitable |- style="vertical-align:top" | <syntaxhighlight lang="objectpascal"> GENERIC INTERFACE G(F_1, ..., F_n); Body END G. INTERFACE I = G(A_1, ..., A_n) END I. </syntaxhighlight> || <syntaxhighlight lang="objectpascal"> INTERFACE I; IMPORT A_1 AS F_1, ..., A_n AS F_n; Body END I. </syntaxhighlight> |} 这里的<code>G</code>是命名这个泛型接口的一个标识符,<code>F_1, ..., F_n</code>是叫做<code>G</code>的形式导入的标识符的一个列表,而<code>Body</code>同在非泛型接口中一样,是一序列导入和随后的一序列声明。这里的<code>I</code>是这个实例的名字,而<code>A_1, ..., A_n</code>是<code>G</code>的形式导入要绑定的实际接口的一个列表。 泛型模块及其实例有如下左侧的形式,并等价于右侧定义的普通模块: {| class = wikitable |- style="vertical-align:top" | <syntaxhighlight lang="modula2"> GENERIC MODULE G(F_1, ..., F_n); Body END G. MODULE I EXPORTS E = G(A_1, ..., A_n) END I. </syntaxhighlight> || <syntaxhighlight lang="modula2"> MODULE I EXPORTS E; IMPORT A_1 AS F_1, ..., A_n AS F_n; Body END I. </syntaxhighlight> |} 这里的<code>G</code>是命名这个泛型模块的一个标识符,<code>F_1, ..., F_n</code>是叫做<code>G</code>的形式导入的标识符的一个列表,而<code>Body</code>同在非泛型模块中一样,是一序列的导入和随后的一个块。这里的<code>I</code>是这个实例的名字,<code>E</code>是由<code>M</code>导出的接口的一个列表,而<code>A_1, ..., A_n</code>是<code>G</code>的形式导入要绑定的实际接口的一个列表。<code>EXPORTS E</code>可以省略,在这种情况下它缺省为<code>EXPORTS I</code>。泛型模块自身没有导出,它们只在被实例化时提供。 [[泛型编程|泛型]]接口及其对应的泛型模块(与[[模板 (C++)|C++模板]]一样)可以轻松定义和使用[[抽象数据类型]],但粒度在模块级别,裸露的类型<code>INTEGER</code>或<code>REAL</code>不能使用,相比之下,它们在C++模板中可以使用。例如,可以定义泛型[[堆栈]]: <syntaxhighlight lang="objectpascal"> GENERIC INTERFACE Stack(Elem); (* 这里的Elem.T不是开放数组类型 *) TYPE T <: REFANY; PROCEDURE Create(): T; PROCEDURE Push(VAR s: T; x: Elem.T); PROCEDURE Pop(VAR s: T): Elem.T; END Stack. </syntaxhighlight> <syntaxhighlight lang="modula2"> 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 > 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. </syntaxhighlight> 然后使用接口例如<code>IntegerElem</code>,对其进行实例化,只要这个接口定义了泛型模块所需的属性即可: <syntaxhighlight lang="objectpascal"> INTERFACE IntegerElem; TYPE T = INTEGER; END IntegerElem. INTERFACE IntStack = Stack(IntegerElem) END IntStack. </syntaxhighlight> <syntaxhighlight lang="modula2"> MODULE IntStack = Stack(IntegerElem) END IntStack. </syntaxhighlight> ===面向对象=== Modula-3下的[[面向对象编程]],经常采用部份不透明类型,它通过如下惯用法来声明:{{code|2="objectpascal"|1=Type T <: Public; Public = OBJECT ... END}},这里的不透明类型<code>T</code>,支持在随后定义的对象类型中的字段和方法,而不会披露<code>T</code>的确切结构,也不披露<code>T</code>可能支持的其他方法。 下面的例子,定义一个接口<code>Person</code>,具有按习惯约定命名的两个类型,导出类型<code>T</code>,和公开对象类型<code>Public</code>,这里声明了<code>T</code>是<code>Public</code>的不透明[[子类型]],<code>Public</code>具有两个方法<code>getAge()</code>和<code>init()</code>;随后是<code>Person</code>接口的完全实现: <syntaxhighlight lang="objectpascal"> INTERFACE Person; TYPE T <: Public; Public = OBJECT METHODS getAge(): INTEGER; init(name: TEXT; age: INTEGER): T; END; END Person. </syntaxhighlight> <syntaxhighlight lang="modula2"> 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. </syntaxhighlight> 要建立一个新的<code>Person.T</code>对象,要使用内建操作<code>NEW()</code>,并调用方法<code>init()</code>: <syntaxhighlight lang="modula2"> VAR jim := NEW(Person.T).init("Jim", 25); </syntaxhighlight> 下面是整数堆栈的例子: <syntaxhighlight lang="objectpascal"> INTERFACE IntegerStack; TYPE T <: Public OBJECT; Public = OBJECT METHODS init(): T; push(x: INTEGER); pop(): INTEGER; END; END IntegerStack. </syntaxhighlight> <syntaxhighlight lang="modula2"> 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 > 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. </syntaxhighlight> 创建新堆栈在这里的部份不透明类型实现下为:{{code|2="modula2"|1=VAR myStack := NEW(IntegerStack.T).init()}},在上述的不透明类型实现下为:{{code|2="modula2"|1=VAR myStack := IntStack.Create()}}。而压入一个数在这里的部份不透明类型实现下为:{{code|2="modula2"|1=myStack.push(8)}},在上述的不透明类型实现下为:{{code|2="modula2"|1=IntStack.Push(myStack, 8)}}。 ===多线程=== 语言支持多[[线程]]和在线程间的[[同步 (计算机科学)|同步]]。在[[运行时库]](m3core)中有一个必要接口叫做<code>Thread</code>,它支持采用[[分叉会合模型]]的多线程应用。这里还预定义不透明类型<code>[[互斥锁|MUTEX]]</code>被用来同步多个线程,并保护共享数据免于具有可能损害或竞争条件的同时访问。<code>MUTEX</code>是一个对象,因此可以从它派生其他对象。 <code>LOCK</code>语句有如下形式:{{code|2="modula2"|1=LOCK mu DO S END}},这里的<code>S</code>是一个语句,而<code>mu</code>是一个表达式,它等价于: <syntaxhighlight lang="objectpascal"> WITH m = mu DO Thread.Acquire(m); TRY S FINALLY Thread.Release(m) END END </syntaxhighlight> 这里的<code>m</code>表示不出现在<code>S</code>中的一个变量。<code>LOCK</code>语句介入一个[[互斥锁]]要锁定的块,并暗含着在代码执行轨迹离开这个块时的解锁它。 ===不安全标记=== 某些功能被认为是不安全的,编译器无法再保证结果是[[一致性]]的(例如,当与C编程语言交接时)。在<code>INTERFACE</code>或<code>MODULE</code>前面加上前缀关键字<code>UNSAFE</code>,可用于告诉编译器启用语言的某些不安全的低级功能。例如,在<code>UNSAFE</code>模块中,使用<code>LOOPHOLE()</code>将整数的诸位元复制成浮点<code>REAL</code>数,使用<code>DISPOSE()</code>释放无跟踪的内存。 一个接口是内在安全的,如果在安全模块中使用这个接口,无法产生不核查的运行时间错误。如果导出一个安全接口的所有的模块都是安全的,编译器保证这个接口的内在安全性。如果导出一个安全接口的任何模块是不安全的,就要编程者而非编译器,去做这种保证了。安全接口导入不安全接口,或安全模块导入或导出不安全接口,都是静态错误。导入不安全接口的编译单元本身必定也是不安全的。 ==对其他编程语言的影响== 尽管Modula-3没有获得主流地位,DEC-SRC M3发行的某些部份做到了。可能最有影响的一部份是网络对象库,它形成了[[Java]]包括了网络协议的最初的[[Java远程方法调用|远程方法调用]](RMI)实现的基础。在Sun从[[CORBA|通用对象请求代理架构]](CORBA)标准转移到[[通用对象请求代理间通信协议|基于IIOP的协议]]的时候才被放弃。Java关于远程对象的垃圾收集的文档仍提及了Modula-3网络对象为其先驱性工作<ref>[http://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmi-arch4.html ''Garbage Collection of Remote Objects''] {{Wayback|url=http://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmi-arch4.html |date=20211217025836 }}, Java Remote Method Invocation Documentation for Java SE 8.</ref> 。 [[Python]]从Modula-3借鉴了模块系统、例外系统和关键字参数,Python的类机制受到C++和Modula-3的启发<ref>{{cite web|url=https://docs.python.org/2/tutorial/classes.html#classes|title=The Python Tutorial - Classes|quote=It is a mixture of the class mechanisms found in C++ and Modula-3. ……As in Modula-3, there are no shorthands for referencing the object’s members from its methods: the method function is declared with an explicit first argument representing the object, which is provided implicitly by the call. ……I would use Modula-3 terms, since its object-oriented semantics are closer to those of Python than C++, but I expect that few readers have heard of it.|access-date=2018-07-01|archive-date=2020-12-03|archive-url=https://web.archive.org/web/20201203031225/https://docs.python.org/2/tutorial/classes.html#classes}}</ref>。[[Nim]]利用了Modula-3的某些方面比如有跟踪和无跟踪的指针。 ==引用== {{reflist|2}} ==外部链接== *{{Official website|www.modula3.org}} *{{GitHub|modula3|Modula3}} *[http://www.opencm3.net CM3 Implementation Website] {{Wayback|url=http://www.opencm3.net/ |date=20210624210729 }} *[https://web.archive.org/web/20050220025439/http://www.research.compaq.com/SRC/modula-3/html/home.html Modula-3 Home Page] (mirror) *[https://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-52.html Modula-3: Language definition] {{Wayback|url=https://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-52.html |date=20210628234936 }} *[https://web.archive.org/web/20120227030339/http://www.professeurs.polymtl.ca/michel.dagenais/pkg/BDAM3alpha.ps Building Distributed OO Applications: Modula-3 Objects at Work. Michel R. Dagenais. Draft Version (January 1997)] *[http://csis.pace.edu/~bergin/M3text Object-Oriented Data Abstraction in Modula-3. Joseph Bergin (1997)] {{Wayback|url=http://csis.pace.edu/~bergin/M3text |date=20110612105456 }} {{程序设计语言}} {{Authority control}} [[Category:面向对象的编程语言]] [[Category:系統程式語言]] [[Category:Modula程式語言家族]] [[Category:1988年建立的程式語言]]
摘要:
请注意,所有对Local Chinese Wikipedia的贡献均可能会被其他贡献者编辑、修改或删除。如果您不希望您的文字作品被随意编辑,请不要在此提交。
您同时也向我们承诺,您提交的内容为您自己所创作,或是复制自公共领域或类似自由来源(详情请见
Project:著作权
)。
未经许可,请勿提交受著作权保护的作品!
取消
编辑帮助
(在新窗口中打开)
导航菜单
个人工具
未登录
讨论
贡献
创建账号
登录
命名空间
页面
讨论
大陆简体
不转换
简体
繁體
大陆简体
香港繁體
澳門繁體
大马简体
新加坡简体
臺灣正體
查看
阅读
编辑
查看历史
更多
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
工具
链入页面
相关更改
特殊页面
页面信息