Open main menu
Home
Random
Recent changes
Special pages
Community portal
Preferences
About Wikipedia
Disclaimers
Incubator escapee wiki
Search
User menu
Talk
Dark mode
Contributions
Create account
Log in
Editing
Modula-3
(section)
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
==Language features== ===Modularity=== First and foremost, all compiled units are either <code>INTERFACE</code> or implementation <code>MODULE</code>s, of one flavor or another. An interface compiled unit, starting with the keyword <code>INTERFACE</code>, defines constants, types, variables, exceptions, and procedures. The implementation module, starting with the keyword <code>MODULE</code>, provides the code, and any further constants, types, or variables needed to implement the interface. By default, an implementation module will implement the interface of the same name, but a module may explicitly <code>EXPORT</code> to a module not of the same name. For example, the main program exports an implementation module for the Main interface. <syntaxhighlight lang="modula2"> MODULE HelloWorld EXPORTS Main; IMPORT IO; BEGIN IO.Put("Hello World\n") END HelloWorld. </syntaxhighlight> Any compiled unit may <code>IMPORT</code> other interfaces, although circular imports are forbidden. This may be resolved by doing the import from the implementation MODULE. The entities within the imported module may be imported, instead of only the module name, using the <code>FROM Module IMPORT Item [, Item]*</code> syntax: <syntaxhighlight lang="modula2"> MODULE HelloWorld EXPORTS Main; FROM IO IMPORT Put; BEGIN Put("Hello World\n") END HelloWorld. </syntaxhighlight> Typically, one only imports the interface, and uses the 'dot' notation to access the items within the interface (similar to accessing the fields within a record). A typical use is to define one [[data structure]] (record or object) per interface along with any support procedures. Here the main type will get the name 'T', and one uses as in <code>MyModule.T</code>. In the event of a name collision between an imported module and other entity within the module, the reserved word <code>AS</code> can be used as in <code>IMPORT CollidingModule AS X;</code> ===Safe vs unsafe=== Some ability is deemed unsafe, where the compiler can no longer guarantee that results will be consistent; for example, when interfacing to the [[C (programming language)|C]] language. The keyword <code>UNSAFE</code> prefixed in front of <code>INTERFACE</code> or <code>MODULE</code>, may be used to tell the compiler to enable certain low level features of the language. For example, an unsafe operation is bypassing the type system using <code>LOOPHOLE</code> to copy the bits of an integer into a floating point <code>REAL</code> number. An interface that imports an unsafe module must also be unsafe. A safe interface may be exported by an unsafe implementation module. This is the typical use when interfacing to external [[Library (computing)|libraries]], where two interfaces are built: one unsafe, the other safe. ===Generics=== A generic interface and its corresponding generic module, prefix the <code>INTERFACE</code> or <code>MODULE</code> keyword with <code>GENERIC</code>, and take as formal arguments other interfaces. Thus (like [[Template (C++)|C++ templates]]) one can easily define and use abstract data types, but unlike [[C++]], the granularity is at the module level. An interface is passed to the generic interface and implementation modules as arguments, and the compiler will generate concrete modules. For example, one could define a GenericStack, then instantiate it with interfaces such as <code>IntegerElem</code>, or <code>RealElem</code>, or even interfaces to Objects, as long as each of those interfaces defines the properties needed by the generic modules. The bare types <code>INTEGER</code>, or <code>REAL</code> can't be used, because they are not modules, and the system of generics is based on using modules as arguments. By comparison, in a C++ template, a bare type would be used. '''FILE: IntegerElem.i3''' <syntaxhighlight lang="modula2" highlight="1"> INTERFACE IntegerElem; CONST Name = "Integer"; TYPE T = INTEGER; PROCEDURE Format(x: T): TEXT; PROCEDURE Scan(txt: TEXT; VAR x: T): BOOLEAN; END IntegerElem. </syntaxhighlight> '''FILE: GenericStack.ig''' <syntaxhighlight lang="modula2" highlight="1"> GENERIC INTERFACE GenericStack(Element); (* Here Element.T is the type to be stored in the generic stack. *) TYPE T = Public OBJECT; Public = OBJECT METHODS init(): TStack; format(): TEXT; isEmpty(): BOOLEAN; count(): INTEGER; push(elm: Element.T); pop(VAR elem: Element.T): BOOLEAN; END; END GenericStack. </syntaxhighlight> '''FILE: GenericStack.mg''' <syntaxhighlight lang="modula2" highlight="1"> GENERIC MODULE GenericStack(Element); < ... generic implementation details... > PROCEDURE Format(self: T): TEXT = VAR str: TEXT; BEGIN str := Element.Name & "Stack{"; FOR k := 0 TO self.n -1 DO IF k > 0 THEN str := str & ", "; END; str := str & Element.Format(self.arr[k]); END; str := str & "};"; RETURN str; END Format; < ... more generic implementation details... > END GenericStack. </syntaxhighlight> '''FILE: IntegerStack.i3''' <syntaxhighlight lang="modula2"> INTERFACE IntegerStack = GenericStack(IntegerElem) END IntegerStack. </syntaxhighlight> '''FILE: IntegerStack.m3''' <syntaxhighlight lang="modula2"> MODULE IntegerStack = GenericStack(IntegerElem) END IntegerStack. </syntaxhighlight> ===Traceability=== Any identifier can be traced back to where it originated, unlike the 'include' feature of other languages. A compiled unit must import identifiers from other compiled units, using an <code>IMPORT</code> statement. Even enumerations make use of the same 'dot' notation as used when accessing a field of a record. <syntaxhighlight lang="modula2" highlight="1"> INTERFACE A; TYPE Color = {Black, Brown, Red, Orange, Yellow, Green, Blue, Violet, Gray, White}; END A; </syntaxhighlight> <syntaxhighlight lang="modula2"> MODULE B; IMPORT A; FROM A IMPORT Color; VAR aColor: A.Color; (* Uses the module name as a prefix *) theColor: Color; (* Does not have the module name as a prefix *) anotherColor: A.Color; BEGIN aColor := A.Color.Brown; theColor := Color.Red; anotherColor := Color.Orange; (* Can't simply use Orange *) END B. </syntaxhighlight> ===Dynamic allocation=== Modula-3 supports the allocation of data at [[Runtime (program lifecycle phase)|runtime]]. There are two kinds of memory that can be allocated, <code>TRACED</code> and <code>UNTRACED</code>, the difference being whether the [[Garbage collection (computer science)|garbage collector]] can see it or not. <code>NEW()</code> is used to allocate data of either of these classes of memory. In an <code>UNSAFE</code> module, <code>DISPOSE</code> is available to free untraced memory. ===Object-oriented=== Object-oriented programming techniques may be used in Modula-3, but their use is not a requirement. Many of the other features provided in Modula-3 (modules, generics) can usually take the place of object-orientation. Object support is intentionally kept to its simplest terms. An object type (termed a "class" in other object-oriented languages) is introduced with the <code>OBJECT</code> declaration, which has essentially the same syntax as a <code>RECORD</code> declaration, although an object type is a reference type, whereas RECORDs in Modula-3 are not (similar to structs in C). Exported types are usually named T by convention, and create a separate "Public" type to expose the methods and data. For example: <syntaxhighlight lang="modula2" highlight="1"> INTERFACE Person; TYPE T <: Public; Public = OBJECT METHODS getAge(): INTEGER; init(name: TEXT; age: INTEGER): T; END; END Person. </syntaxhighlight> This defines an interface <code>Person</code> with two types, <code>T</code>, and <code>Public</code>, which is defined as an object with two methods, <code>getAge()</code> and <code>init()</code>. <code>T</code> is defined as a subtype of <code>Public</code> by the use of the <code><:</code> operator. To create a new <code>Person.T</code> object, use the built in procedure <code>NEW</code> with the method <code>init()</code> as <syntaxhighlight lang="modula2"> VAR jim := NEW(Person.T).init("Jim", 25); </syntaxhighlight> === Revelation === Modula-3's <code>REVEAL</code> construct provides a conceptually simple and clean yet very powerful mechanism for hiding implementation details from clients, with arbitrarily many levels of ''friendliness''. A full revelation of the form <code>REVEAL T = V</code> can be used to show the full implementation of the <code>Person</code> interface from above. A partial revelation of the form <code>REVEAL T <: V</code> merely reveals that T is a supertype of V without revealing any additional information on T.<ref>{{Cite journal |last1=Cardelli |first1=Luca |last2=Donahue |first2=James |last3=Glassman |first3=Lucille |last4=Jordan |first4=Mick |last5=Kalsow |first5=Bill |last6=Nelson |first6=Greg |date=August 1992 |title=Modula-3 language definition |url=https://dl.acm.org/doi/10.1145/142137.142141 |journal=ACM SIGPLAN Notices |language=en |volume=27 |issue=8 |pages=15β42 |doi=10.1145/142137.142141 |issn=0362-1340}}</ref> <syntaxhighlight lang="modula2"> MODULE Person; REVEAL T = Public BRANDED OBJECT name: TEXT; (* These two variables *) age: INTEGER; (* are private. *) 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> Note the use of the <code>BRANDED</code> keyword, which "brands" objects to make them unique as to avoid structural equivalence. <code>BRANDED</code> can also take a string as an argument, but when omitted, a unique string is generated for you. Modula-3 is one of a few programming languages which requires external references from a module to be strictly qualified. That is, a reference in module <code>A</code> to the object <code>x</code> exported from module <code>B</code> must take the form <code>B.x</code>. In Modula-3, it is impossible to import ''all exported names'' from a module. Because of the language's requirements on name qualification and [[method overriding]], it is impossible to break a working program simply by adding new declarations to an interface (any interface). This makes it possible for large programs to be edited concurrently by many programmers with no worries about naming conflicts; and it also makes it possible to edit core language libraries with the firm knowledge that no extant program will be ''broken'' in the process. ===Exceptions=== [[Exception handling]] is based on a <code>TRY</code>...<code>EXCEPT</code> block system, which has since{{citation needed|date=March 2014}} become common. One feature that has not been adopted in other languages{{citation needed|date=March 2014}}, with the notable exceptions of [[Delphi (programming language)|Delphi]], [[Python (programming language)|Python]][https://www.python.org/doc/faq/general/#why-was-python-created-in-the-first-place], [[Scala (programming language)|Scala]][http://scala.epfl.ch] and [[Visual Basic.NET]], is that the <code>EXCEPT</code> construct defined a form of [[switch statement]] with each possible exception as a case in its own EXCEPT clause. Modula-3 also supports a <code>LOOP</code>...<code>EXIT</code>...<code>END</code> construct that loops until an <code>EXIT</code> occurs, a structure equivalent to a simple loop inside a <code>TRY</code>...<code>EXCEPT</code> clause. ===Multi-threaded=== The language supports the use of multi-threading, and synchronization between threads. There is a standard module within the [[runtime library]] (''m3core'') named Thread, which supports the use of multi-threaded applications. The Modula-3 runtime may make use of a separate thread for internal tasks such as garbage collection. A built-in data structure <code>[[Lock (computer science)|MUTEX]]</code> is used to synchronize multiple threads and protect data structures from simultaneous access with possible corruption or race conditions. The <code>LOCK</code> statement introduces a block in which the mutex is locked. Unlocking a <code>MUTEX</code> is implicit by the code execution locus's leaving the block. The <code>MUTEX</code> is an object, and as such, other objects may be derived from it. For example, in the [[input/output]] (I/O) section of the library ''libm3'', readers and writers (Rd.T, and Wr.T) are derived from MUTEX, and they lock themselves before accessing or modifying any internal data such as buffers. <!-- Needs expansion --> ===Summary=== In summary, the language features: *[[Modularity (programming)|Modules]] and [[Interface (computer science)|interfaces]] *Explicit marking of unsafe code *[[Generic programming|Generics]] *Automatic [[Garbage collection (computer science)|garbage collection]] *[[strongly-typed programming language|Strong typing]], structural equivalence of types *[[Object (computer science)|Objects]] *[[Exception handling|Exceptions]] *[[Thread (computer science)|Threads]] In ''Systems Programming with Modula-3'', four essential points of the language design are intensively discussed. These topics are: structural vs. name equivalence, subtyping rules, generic modules, and parameter modes like <code>READONLY</code>.
Edit summary
(Briefly describe your changes)
By publishing changes, you agree to the
Terms of Use
, and you irrevocably agree to release your contribution under the
CC BY-SA 4.0 License
and the
GFDL
. You agree that a hyperlink or URL is sufficient attribution under the Creative Commons license.
Cancel
Editing help
(opens in new window)