home *** CD-ROM | disk | FTP | other *** search
Text File | 2002-05-03 | 59.8 KB | 1,340 lines |
- Path: senator-bedfellow.mit.edu!bloom-beacon.mit.edu!howland.erols.net!sunqbc.risq.qc.ca!carnaval.risq.qc.ca.POSTED!not-for-mail
- From: Michel Dagenais <michel.dagenais@polymtl.ca>
- Newsgroups: comp.lang.modula3,news.answers,comp.answers
- Subject: Modula-3 Frequently Asked Questions (FAQ)
- Supersedes: <m3_1017756600@vlsi.polymtl.ca>
- Followup-To: comp.lang.modula3
- Organization: Ecole Polytechnique de Montreal
- Lines: 1322
- Sender: dagenais@m3.polymtl.ca
- Approved: news-answers-request@MIT.Edu
- Expires: 15 Jun 2002 13:10:00 GMT
- Message-ID: <m3_1020345000@vlsi.polymtl.ca>
- Date: Thu, 02 May 2002 12:51:37 GMT
- NNTP-Posting-Host: 132.207.4.60
- X-Trace: carnaval.risq.qc.ca 1020343897 132.207.4.60 (Thu, 02 May 2002 08:51:37 EDT)
- NNTP-Posting-Date: Thu, 02 May 2002 08:51:37 EDT
- Xref: senator-bedfellow.mit.edu comp.lang.modula3:12299 news.answers:229479 comp.answers:49803
-
- Archive-name: Modula-3-faq
-
- Michel Dagenais Michel Dagenais, GNU General Public License, 1998-2001
-
-
- michel.dagenais@polymtl.ca
- Ecole Polytechnique
- C.P. 6079, Succ. Centre-Ville
- Montreal, Quebec, H3C 3A7
-
- Modula-3 Frequently asked questions and answers
-
- Maintained by Michel Dagenais ([1]michel.dagenais@polymtl.ca),
- suggestions are most welcome. Last updated January 15 2002. The latest
- copy of this FAQ may be obtained from the [2]Polytechnique Montreal
- Modula-3 Home.
-
- Introduction
-
- What is Modula-3?
-
- Modula-3 is a systems programming language that descends from Mesa,
- Modula-2, Cedar, and Modula-2+. It also resembles its cousins Object
- Pascal, Oberon, and Euclid.
-
- The goal of Modula-3 is to be as simple and safe as it can be while
- meeting the needs of modern systems programmers. Instead of exploring
- new features, we studied the features of the Modula family of
- languages that have proven themselves in practice and tried to
- simplify them into a harmonious language. We found that most of the
- successful features were aimed at one of two main goals: greater
- robustness, and a simpler, more systematic type system.
-
- Modula-3 retains one of Modula-2's most successful features, the
- provision for explicit interfaces between modules. It adds objects and
- classes, exception handling, garbage collection, lightweight processes
- (or threads), and the isolation of unsafe features.
-
- Where is Modula-3 used? Is it used in industry?
-
- A number of programming teams selected Modula-3 for industrial and
- research projects, and for teaching. It encourages good programming
- practices and comes with excellent libraries for distributed
- programming and graphical user interfaces. A non exhaustive list is
- available at [3][Modula-3 at Work].
-
- Is commercial support available?
-
- Critical Mass Corporation used to offer their own version of SRC
- Modula-3, CM3, an integrated development environment for Modula-3,
- [4]Reactor, as well as training and consulting services.
-
- Olaf Wagner from [5]Elego Software Solutions is now maintaining [6]CM3
- as an open source package and offers commercial support.
-
- Where can I get documents and information on Modula-3?
-
- A [7]concise bibliography and a more complete [8]bibliography describe
- Modula-3 related books, technical reports, and papers. The definition
- of Modula-3 is contained in: [9]"System Programming with Modula-3"
- also known as SPwM3. Sam Harbison has written a more tutorial book
- titled [10]Modula3.
-
- Three main Web servers contain Modula-3 related information:
- [11]www.m3.org, [12]DEC SRC Modula-3 home page, and [13]Ecole
- Polytechnique de MontrΘ Modula-3 home page.
-
- The Usenet newsgroup comp.lang.modula3 is the official meeting place
- for Modula-3 related discussions.
-
- Is Modula-3 a superset of Modula-2?
-
- No; valid Modula-2 programs are not valid Modula-3 programs. However,
- there is a tool to help convert Modula-2 programs to Modula-3.
-
- Comparisons between Modula-3 and other languages?
-
- From: laszlo@post.ifi.uni-klu.ac.at (Laszlo BOESZOERMENYI)
-
- A Comparison of Modula-3 and Oberon-2 by myself in Structured
- Programming 1993, 14:15-22
-
- From: nayeri@gte.com
-
- Robert Henderson, Benjamin Zorn, A Comparison of Object-Oriented
- Programming in Four Modern Languages, Department of Computer Science,
- University of Colorado, Boulder, Colorado, [14]Technical Report
- CU-CS-641-93.
-
- The paper evaluates Oberon, Modula-3, Sather, and Self in the context
- of object-oriented programming. While each of these programming
- languages provide support for classes with inheritance, dynamic
- dispatch, code reuse, and information hiding, they do so in very
- different ways and with varying levels of efficiency and simplicity. A
- single application was coded in each language and the experience
- gained forms the foundation on which the subjective critique is based.
-
- What implementations are available, how do they compare?
-
- All implementations are based on [15]DEC SRC Modula-3. [16]Critical
- Mass offered an improved version with commercial support. It features
- incremental garbage collection on NT, and a few additional packages
- like ODBC database access. This is now [17]open sourced and maintained
- by Olaf Wagner. Ecole Polytechnique de MontrΘal has been maintaining
- an [18]updated distribution. It features integrated documentation, and
- NT support through the gcc cygwin compiler
-
- Can I contribute Modula-3 software?
-
- Contributions are most welcome. The primary contact to offer
- contributions is comp.lang.modula3.
-
- [19]The Ecole Polytechnique de MontrΘal Modula-3 distribution is the
- most regularly updated and may be a good place to submit your
- contribution.
-
- Why use Modula-3?
-
- Here is what John Polstra, author of the popular CVSup, replied:
- Subject: Re: SUP on sup.freebsd.org
- Date:Wed, 06 Nov 1996 12:31:26 -0800
- From:John Polstra
- > Erhm, why on earth did you chose Modula3 ??
-
- Modula-3 really is a different language, designed specifically for
- systems programming by some extremely competent and experienced people
- who knew what they were doing.
- > Oh and yes I have seen apps written in modula3, all of which was
- > horrible performers, and impossible to port to new platforms, so
- > the management decide a complete rewrite in, guess what, C!
-
- Are you sure it was Modula-3? The SRC Modula-3 compiler supports about
- 25 different platforms.
-
- Plenty of real world apps (*big* ones) have been written in Modula-3,
- and they perform pretty well. There's also the SPIN OS project
- [20][SPIN] at University of Washington, in which the kernel was
- written in Modula-3. It performs well, too.
-
- Now, you can always argue that a program would be somewhat faster and
- somewhat smaller if it had been written in C. Hey, guess what? I was
- around when Unix V6 came out, and the same stuff was written about it.
- Just substitute "C" for "Modula-3" and "assembly language" for "C".
- The answer is the same in both cases: Unix would not exist as we know
- it today if it had been written in assembly language. CVSup would not
- exist as we know it today if it had been written in C (or C++, for
- that matter).
-
- OK, so why on earth did I choose Modula-3? In no particular order:
- 1. I needed application level threads, and threads are an integral
- part of the Modula-3 language. About the only reasonable
- alternative was to use pthreads with C or C++. But pthreads was
- not well supported under FreeBSD at that time.
- 2. I needed a graphical display during development so that I could
- monitor the 3 client threads as they were running, debug them,
- appraise their relative performance, and find the bottlenecks.
- Modula-3 has a very nice toolkit for creating GUIs quickly and
- painlessly. (OK, so the scrollbars are as ugly as sin.)
- 3. Modula-3 is a compiled language that is reasonably efficient.
- 4. I needed to use some low level system functions, e.g., mapping
- files into memory. Modula-3 provides good access to such
- functions, and it is quite easy to add interfaces to foreign
- libraries such as "libz".
- 5. Modula-3 has good support for networking.
- 6. It is a mature and stable language that has been used in a number
- of serious, large projects. The language and compiler have been
- stable for about 5 years, which is more than you can say for C++.
- 7. It has nice support for object oriented programming, including a
- good type system, a nice exception model, and a modern
- high-performance garbage collector. These traits, IMHO, contribute
- powerfully to producing well-structured, maintainable programs.
- Now before you label me an unstudly OO weenie, please consider
- this. I've been programming in C professionally for 19 years. I
- made my living for many years writing C compilers and related
- tools such as assemblers, linkers, and disassemblers. I still use
- C and C++ when I feel they are appropriate for a project, not to
- mention when I have to because that's what the client wants to
- use. I have experience programming in many many different
- languages. Different languages are good for different things. I
- still like programming in C (and C++ for some things), but I'm
- glad I didn't use it for CVSup.
- 8. I had just come off a huge 3+ year C++ project. During that time,
- I learned just how much C++ sucks. I did not feel like doing it
- again right away "for fun."
- 9. I have spent my entire professional career getting paid to use the
- wrong tools, because, e.g., the manager read that C++ was
- "popular." For once, just once, on a _hobby_ project, I decided I
- was going to use the tool I felt was the best for the job at hand.
- I thought about it long and hard, evaluated several options (C and
- C++ among them), and eventually chose Modula-3. I have never
- regretted that decision.
-
- Any questions? :-)
- John
- - --
- John Polstra jdp@polstra.com
- John D. Polstra & Co., Inc. Seattle, Washington USA
- "Self-knowledge is always bad news." -- John Barth
-
- Troubleshooting
-
- Why program receives a SEGV signal under the debugger?
-
- The garbage collector on some platforms uses the SEGV (segmentation
- violation) signal to detect modified portions of the dynamically
- allocated space. It is possible to disable this feature or to inform
- the debugger to let these signals propagate. See the [21]m3gdb
- documentation.
-
- Problems with threads, fork and VTALARM?
-
- The threads are implemented using the virtual timer interrupt.
- Normally, the run time environment will catch the interrupt and
- determine if thread switching is appropriate. However, if a new
- process is created with fork, it will have the virtual timer activated
- and no interrupt handler to receive it, resulting in a core dump. If
- you use the standard procedure Process.Create to fork new processes,
- this will be handled automatically for you. If you insist on using
- fork, you need to disable the timer, fork and then reenable the timer.
-
- X libraries not found?
-
- The position of X libraries is stored, for instance for pre-compiled
- PM3 LINUXELF binaries, in the template file m3config/src/LINUXELF as
- well as in X11/LINUXELF/.M3EXPORTS (m3build/templates/LINUXELF, and
- X11R4/LINUXELF/.M3EXPORTS for SRC-M3). Thus you may want to edit these
- files if your X libraries are located in an uncommon place.
-
- What means Missing RTHooks or similar messages?
-
- The standard library, libm3, is not included by default. You need in
- your m3makefiles to import("libm3") or to import a library which
- imports libm3. Otherwise, messages about run time procedures such as
- RTHooks not being available are produced.
-
- M3build versus Make or why m3 does not work?
-
- The Modula-3 compiler m3 does a much finer grained dependency analysis
- than possible with make. For this reason, a very flexible front end,
- m3build, reads the program description files, m3makefile, and
- generates the commands required to compile and link Modula-3 programs
- and libraries. The m3makefile content is documented in the m3build
- documentation. Calling the m3 compiler directly is difficult and thus
- not recommended, especially on PM3 where it is now merged with
- m3build.
-
- Why are exceptions raised by X or Network Objects applications?
-
- Graphical applications (based on Trestle/X Windows) raise the
- TrestleComm.Failure exception when the DISPLAY environment variable is
- incorrect or the X server is refusing the connection. They raise
- MachineIDPosix.Failure if the network configuration files are
- incorrectly set up, especially on LINUX; /etc/hosts must contain at
- least a loopback address (127.0.0.1) and the /etc/rc scripts an
- appropriate ifconfig command (/etc/ifconfig lo 127.0.0.1; /etc/route
- add 127.0.0.1). Applications with Network Objects may also raise
- exceptions or consume all the CPU time available when the network
- configuration files are incorrect.
-
- What is the story with Trestle and OpenWindows?
-
- Mark Manasse says:
-
- I think that the OpenWindows release should be enough (no need to get
- the MIT X release), although there are a few things in Trestle that
- trigger devastating bugs in OpenWindows. But the only library we
- depend on is Xlib, R4 or later.
-
- The main thing I know that crashes OW 2.0 is the code where we call
- GrabKey specifying AnyKey. You can either loop over all of the keys,
- or you can just comment out the call; programs won't run exactly the
- same, but you probably won't notice the difference.
-
- Why so many problems installing on Solaris?
-
- These notes were contributed by (simon.hood@umist.ac.uk) while
- installing PM3-1.1.14 on a Sun Ultra 5 running Solaris 2.8. They
- describe various problems and their solution or workaround.
-
- The installation of PM3 on Solaris systems is particularly prone to
- problems as these systems tend to be an unpredictable (from the point
- of view of the PM3 people) mixture of Sun and Gnu software --- Sun do
- not bundle a C compiler with the operating system.
-
- My machine has gcc version 2.95.2 installed; it has Sun's versions of
- make, ld, as and ar installed within /usr/ccs/bin; Gnu's version of
- these tools are not installed.
-
- My installation was successful, after a bit of fiddling around with
- the configuration/template files and environment variables. Some of
- the fixes are trivial (if you know what you are doing), while others
- --- for me at least --- were not --- I am a Modula 3 novice and far
- from experienced with Solaris.
-
- The issues that arose are:
- * Paths needed to be set to find tools such as make.
- * LD_LIBRARY_PATH needed to be set to ensure libstdc++.a.2.10.0
- and/or libstdc++.so.2.10.0 were found.
- * A link needed to be set so that byacc points to yacc.
- * The configuration for linking needed to be changed since only the
- Sun version of ld was installed, not Gnu's.
- * The build of m3gdb failed to build.
- * The gnuemacs package failed to build.
-
- Initial Problems
-
- Paths
- gcc is usually installed in /usr/local/bin; on a Solaris
- machine, ar, as, make and ld are all in /usr/ccs/bin, by
- default. Hence these must both be on root's path (assuming you
- are installing as root). Neither were; I have not changed any
- paths since installation of Solaris 2.8 on a new machine a few
- days ago.
- This is in addition to /usr/local/pm3/bin, as mentioned by the
- PM3 installation instructions.
-
- byacc/yacc
- The build required byacc. yacc is installed in /usr/ccs/bin; a
- soft link:
-
- lrwxrwxrwx 1 root other 4 Aug 11 15:45 byacc -> yacc
-
-
- solved this problem.
-
- Library Paths
- In addition to /usr/local/pm3/lib/m3 as mentioned by the PM3
- installation instructions LD_LIBRARY_PATH must include
- /usr/local/lib so that libstdc++ (part of the gcc distribution)
- can be found.
-
- CC
- In addition I found that the environment variable CC needed to
- be set to /usr/local/bin/gcc. This is of course mentioned in
- the PM3 installation instructions.
-
- Miscellaneous Questions
-
- Can I get Modula-3 other than by FTP or HTTP?
-
- Prime Time Freeware (PTF) includes Modula-3. PTF is a set of two
- ISO-9660 CDroms filled with 3GB of freeware, issued semi-annually. PTF
- is distributed via bookstores and mail. You can reach PTF using:
-
- Email: ptf@cfcl.com
- Fax: [1] (408) 738 2050
- Voice: [1] (408) 738 4832
- Mail: Prime Time Freeware
- 415-112 N. Mary Ave., Suite 50
- Sunnyvale, CA 94086
- USA
-
- Many Linux CDroms include a copy of the FTP site tsx-11.mit.edu which
- has Linux binaries for Modula-3.
-
- How to call Modula-3 procedures from a C program?
-
- Calling Modula-3 from C is tricky because M3 has a more elaborate
- run-time environment. The simplest solution is to make the main
- program M3 and then call C via EXTERNAL routines. Calling back into M3
- is then relatively straightforward.
-
- Here's an example. It calls the C code to lodge the identity of the M3
- procedure to be called back which avoids having to know the actual
- name used by the linker.
-
- First a little M3 module to be called from C (M3code), then a C module
- called by the M3 main and calling the M3 module (Ccode), and finally
- the main program (Main):
- (* M3code.i3 *)
-
- INTERFACE M3code;
- IMPORT Ctypes;
- PROCEDURE put (a: Ctypes.char_star);
- END M3code.
-
- (* M3code.m3 *)
-
- UNSAFE MODULE M3code;
- IMPORT Ctypes, IO, M3toC;
-
- PROCEDURE put (a: Ctypes.char_star) =
- BEGIN
- IO.Put (M3toC.StoT (a) & "\n");
- END put;
-
- BEGIN
- END M3code.
-
- (* Ccode.i3 *)
-
- <*EXTERNAL*> INTERFACE Ccode;
- IMPORT Ctypes;
- PROCEDURE set (p: PROCEDURE (a: Ctypes.char_star));
- PROCEDURE act (a: Ctypes.char_star);
- END Ccode.
-
- /* Ccode.c */
-
- typedef void (*PROC)();
- static PROC action;
-
- void set (p)
- PROC p;
- {
- action = p; /* register the M3 procedure */
- }
-
- void act (a)
- char *a;
- {
- action (a); /* call the M3 procedure */
- };
-
- (* Main.m3 *)
-
- UNSAFE MODULE Main;
-
- IMPORT Ccode, M3code, M3toC;
-
- BEGIN
- Ccode.set (M3code.put);
- Ccode.act (M3toC.TtoS ("Hello world"));
- END Main.
-
- (* m3makefile *)
-
- import(libm3)
-
- interface ("Ccode")
- c_source ("Ccode")
- module ("M3code")
- implementation("Main")
- program("mixed")
-
- Can Modula-3 code call C++ and vice-versa?
-
- There is no problem to call C++ functions declared as extern C. You
- must use a C++ aware linker (e.g. the C++ compiler). A complete
- example of M3 calling C++ objects, which in turn call M3 callbacks, is
- available in [22]the sgml library.
-
- On some platforms, a call to get the static variables constructors
- called may be required:
-
- From: gwyant@cloyd.East.Sun.COM (Geoffrey Wyant - Sun Microsystems
- Labs BOS)
-
- You must use your C++ compiler as the linker, rather than /bin/cc or
- /bin/ld.
-
- You need to call the function '_main'. The easiest way to do this is
- to have the following set of interfaces and implementations:
-
- INTERFACE CXXMain;
- <*EXTERN "_main"*> CxxMain;
- END CXXMain;
-
- MODULE CXXMain;
- BEGIN
- CxxMain();
- END;
-
- and then import CXXMain into your M3 main module. This will ensure
- that the C++ function _main gets called.
-
- How to copy heap objects?
-
- Deep copies are easily performed using Pickles. An object graph is
- Pickled to a text writer into a TEXT. Then, a copy is created by
- unpickling a new object graph from a text reader created from the
- TEXT.
-
- Shallow copies are less often needed but may be performed with the
- following procedure:
- PROCEDURE Duplicate (r: REFANY): REFANY =
- VAR
- tc := TYPECODE (r);
- n_dims : INTEGER;
- res : REFANY;
- shape : RTHeapRep.ArrayShape;
- BEGIN
- IF (r = NIL) THEN RETURN NIL END;
-
- (* allocate a new object of the same type (and shape) as the old one *)
- RTHeapRep.UnsafeGetShape (r, n_dims, shape);
- IF (n_dims <= 0)
- THEN res := RTAllocator.NewTraced (tc);
- ELSE res := RTAllocator.NewTracedArray (tc, SUBARRAY(shape^, 0, n_dims));
- END;
-
- (* copy the old data into the new object *)
- RTMisc.Copy (RTHeap.GetDataAdr (r), RTHeap.GetDataAdr (res),
- RTHeap.GetDataSize (r));
-
- RETURN res;
- END Duplicate;
-
- How to get output messages to appear immediately (flushing writers)?
-
- Modula-3 Writers are buffered. Thus, you need to issue a Wr.Flush when
- the output should appear immediately, for instance to prompt the user
- for some input. Since this can become annoying, libraries in other
- languages sometimes offer the option of unbuffered writes. In
- Modula-3, an equivalent behavior is obtained with AutoFlushWr which
- gets a background thread to flush a writer at a specified interval.
-
- How to read a single character as soon as typed?
-
- Characters typed on the keyboard are usually buffered. They become
- visible to the reading program only when the buffer is full or after,
- for example, a carriage return is received. This is not specific to
- Modula-3. To access the characters as they are typed, single character
- commands in a full screen editor for example, the input reader must be
- configured properly.
-
- From: [23]rrw1000@cus.cam.ac.uk (Richard Watts)
-
- The POSIX way of doing it is to use tcsetattr(), and here is some code
- that does it under Solaris 2.x (this was written for serial ports, but
- the same thing applies) :
- PROCEDURE Open(port : CHAR; timeout : INTEGER := 30) : T RAISES {Error} =
- VAR
- term : TcPosix.termios;
- file : TEXT;
- fd : T;
- rc : INTEGER;
- BEGIN
- (* Figure out which device we want to open : *)
-
- CASE port OF
- 'A' => file := "/dev/ttya";
- | 'B' => file := "/dev/ttyb";
- ELSE RAISE Error("Invalid port " & Fmt.Char(port) & " specified.\n");
- END;
-
- (* Open it. 700 is a good default mode for serial ports. *)
- fd := Unix.open(M3toC.TtoS(file), Unix.O_RDWR
- , 8_700);
- IF fd = -1 THEN
- RAISE Error("Open() on " & file & " failed.\n");
- END;
-
- (* Get the termios structure for it *)
- rc := TcPosix.tcgetattr(fd, ADR(term));
- IF rc # 0 THEN
- EVAL Unix.close(fd);
- RAISE Error("Couldn't get terminal attributes for " & file & ".\n");
- END;
-
- (* Modify the termios structure *)
-
- (* The default baud rate is right, but we'd better set it anyway
- in case someone left it set up wrong : *)
- rc := TcPosix.cfsetospeed(ADR(term), TcPosix.B9600);
-
- IF rc # 0 THEN
- EVAL Unix.close(fd);
- RAISE Error("Couldn't set output speed for " & file & "\n");
- END;
-
- rc := TcPosix.cfsetispeed(ADR(term), TcPosix.B9600);
-
- IF rc # 0 THEN
- EVAL Unix.close(fd);
- RAISE Error("Couldn't set input speed for " & file & "\n");
- END;
-
- (* Modify the line discipline - reset ECHO and ICANON *)
- term.c_lflag := Word.And( term.c_lflag,
- Word.Not(
- Word.Or(TcPosix.ICANON,
- TcPosix.ECHO)));
- term.c_cc[TcPosix.VMIN] := 0;
- term.c_cc[TcPosix.VTIME] := 0; (* Set up timing right *)
-
- (* Now reset the terminal attributes *)
- rc := TcPosix.tcsetattr(fd, TcPosix.TCSANOW, ADR(term));
-
- IF rc # 0 THEN
- EVAL Unix.close(fd);
- RAISE Error("Can't set attributes for " & file & "\n");
- END;
- RETURN fd;
- END Open;
-
- (TcPosix.i3 is one of my interfaces, not libm3's, and I'll supply it
- if you like, but it's just a wrapper to tcgetattr and friends. The
- baud rate stuff shouldn't be necessary for terminals (or serial
- ports..) ). You should be able to somehow get an Rd.T out of this, I
- think, but it may involve a bit of hacking. The University of
- Cambridge can't have these opinions even if it wants them.
-
- Why is Hello World larger in Modula-3 than in C?
-
- Modula-3 programs are slightly larger than C programs because the
- generated code includes runtime type information, and runtime checks
- for out-of-bound array references and NIL pointers. Many of these
- checks could be removed by a more sophisticated compiler.
-
- The fixed runtime is substantially larger (there is no runtime support
- in C). It contains a garbage collector, a thread runtime, and
- exception support. It is typically placed in a dynamically linked
- library, shared on disk and in memory between all the Modula-3
- programs.
-
- What is SRC Modula-3?
-
- [24]SRC-Modula-3 was built by the DEC Systems Research Center and is
- freely available and redistributable, with source code. In Europe it
- is also available from ftp-i3.informatik.rwth-aachen.de in
- pub/Modula-3. The most recent version is release 3.6
-
- The DEC SRC Modula-3 contains the following:
-
- * A native code compiler: uses the GCC backend; on
- machines/operating systems that have self-describing stacks, an
- optimized exception handling mechanism is provided, on other
- architectures, setjmp/longjmp is used. A very fast integrated
- backend is available on some platforms (currently NT386 and Linux
- i386).
- The compilation system provides for minimal recompilation. Only
- those units that depend on the modified interface item will be
- recompiled.
- * m3build: tool that performs dependency analysis and builds the
- Modula-3 programs and libraries.
- * m3gdb: a Modula-3 aware version of GDB.
- * Several tools for performance and coverage analysis.
- * A large standard library (libm3) providing
- + A multithread, incremental, generational, conservative
- garbage collector
- + Text manipulation.
- + Generic Containers: Lists, Sequences, Tables, SortedLists,
- SortedTables
- + Atoms and Symbolic expressions (Lisp like lists)
- + An extensible stream IO system
- + Typesafe binary object transcription (persistent objects)
- + Operating system interfaces
- + Portable interfaces to the language runtime
- All standard libraries are thread-friendly. Modula-3 can readily
- link with existing C libraries; many libraries including X11R4 and
- various UNIX libraries are available as part of libm3.
- * Several other libraries for designing graphical user interfaces
- and distributed applications.
-
- Why are there strange pragmas for Locking levels and other properties?
-
- The Trestle (ui library) interfaces contain Locking level pragmas. The
- base interfaces (libm3 library) contain SPEC pragmas. These are not
- processed by the compiler. Instead the Extended Static Checker,
- currently under development at DEC SRC, will report on problems
- detected based on the program content and the information specified in
- these pragmas [25][ESC]. The Extended Static Checker is not yet
- available, it may be some time in the future.
-
- Design Issues
-
- Why objects and interfaces?
-
- Allan Heydon on comp.lang.modula3, May 4th 1993:
-
- Modula-3 provides two separate mechanisms for data-hiding: one for
- hiding details about how interfaces are implemented, and the other for
- hiding details about how objects are implemented.
-
- The first data-hiding mechanism is realized by the distinction between
- interfaces and modules. Clients can only import interfaces, so the
- names declared in the modules implementing those interfaces are hidden
- from clients. Note that this mechanism has only two levels; a name is
- either declared in an interface, or it isn't. If a name is only
- declared in a module, it can't be used by a client.
-
- The second data-hiding mechanism is realized by opaque types and
- revelations. A Modula-3 interface may declare an object type to be
- opaque, in which case only a subset of the fields and methods of that
- object are revealed to clients importing the interface. Furthermore,
- the Modula-3 revelation mechanism allows a designer to reveal
- successively more fields and methods of an object in a series of
- interfaces. The fields and methods visible to a client then depends on
- which interfaces the client imports.
-
- The latter mechanism is quite flexible. As opposed to the
- interface/module data-hiding mechanism, opaque types allow you to
- define an arbitrary number of levels at which more and more
- information about the implementation of your object is revealed.
-
- See Sections 2.2.10, 2.4.6, and 2.4.7 of "Systems Programming with
- Modula-3" for more information about opaque types and about partial
- and complete revelations.
-
- What is the purpose of BRANDED and REVEAL?
-
- Allan Heydon writes:
-
- These two keywords are necessary because of two quite different
- features of the language. REVEAL is necessary because Modula-3 has
- opaque types and partial revelations. BRANDED is necessary because the
- Modula-3 type system uses structural equivalence instead of name
- equivalence.
-
- In Modula-3, the concrete structure of a type can be hidden from
- clients in an interface. A common idiom is:
-
- INTERFACE I;
-
- TYPE
- T <: TPublic;
- TPublic = OBJECT
- (* fields *)
- METHODS
- (* methods *)
- END;
-
- END I.
-
- The line "T <: TPublic" introduces the type "I.T" as an opaque subtype
- of the type "I.TPublic". It does not reveal any of the other details
- of the concrete structure of "I.T" to clients. Hence, "I.T" is said to
- be an opaque type. Put another way, the structure of "I.T" is only
- partially revealed to clients.
-
- In addition, it is possible to reveal more of "I.T"'s structure in
- other interfaces, like this:
-
- INTERFACE IRep;
-
- IMPORT I;
-
- TYPE
- TPrivate = I.TPublic OBJECT
- (* more fields *)
- METHODS
- (* more methods *)
- END;
-
- REVEAL
- I.T <: TPrivate;
-
- END IRep.
-
- This interface declares a type "IRep.TPrivate" that is a subtype of
- "I.TPublic". It also asserts that "I.T" is also a subtype of
- "IRep.TPrivate". A client that imports only the interface "I" has
- access only to the fields and methods in "I.TPublic" when accessing an
- object of type "I.T", but a client that imports both "I" and "IRep"
- also has access to the fields and methods in "IRep.TPrivate" when
- accessing an object of type "I.T".
-
- The "REVEAL" statement in this module simply asserts a subtype
- relation. Unlike type declarations, revelations introduce no new
- names. Hence, we could not have used the "TYPE" keyword in this case
- because the type "I.T" has already been declared once (albeit
- opaquely) in interface "I".
-
- Every opaque type must have a complete revelation. A complete
- revelation has the form:
-
- REVEAL
- T = TConcrete;
-
- The revelation specifies that "TConcrete" is the concrete type for the
- opaque type "T".
-
- The Modula-3 type system uses structural equivalence instead of name
- equivalence. This means that two types are equal iff they have the
- same structure. One consequence of this rule is that two types you
- might intend to be distinct may actually be equal. This can have
- unintended effects on the run-time behavior of your program. For
- example, if both types that you expect to be distinct are actually
- structurally equivalent and the two types guard two arms of a TYPECASE
- statement, the arm for the second type will never be taken.
-
- If you want to avoid accidental equalities between two types, you can
- brand one (or both) of them with the BRANDED keyword. A branded type
- is equivalent to no other type, even if it is structurally equivalent
- to some other type. In essence, the BRANDED keyword adds a bit of
- virtual structure to the type that guarantees it will be distinct from
- every other type.
-
- The Modula-3 syntax allows you to supply a text constant as a name for
- the brand. If you don't supply an explicit brand, the compiler will
- make one up; however, the implicit brand invented by the compiler is
- not guaranteed to be chosen deterministically. Hence, explicit brands
- are useful if you are communicating types from one process to another
- and if you want to be sure that the branded type written by one
- process matches the branded type read in by the other.
-
- Any two opaque types in a program must be distinct. Otherwise, it
- would be too easy for clients to accidentally trip over type
- collisions like the TYPECASE example mentioned above. To enforce the
- restriction that all opaque types are distinct, the language requires
- that the type "TConcrete" in the complete revelation above must be a
- branded type.
-
- Can a program recover from running out of virtual memory?
-
- No, this turns out to be quite a thorny problem. I think the best
- thing I can do is by attaching to this message the dialog that went on
- during the "beta test" of the new library interfaces (SRC Research
- Report 113, "Some Useful Modula-3 Interfaces). The parties are Xerox
- PARC's David Goldberg, Hans Boehm, Alan Demers, and David Nichols, and
- SRC's John DeTreville, who designed and implemented the garbage
- collector in SRC Modula-3. The dialog covers many of the issues, and
- apparently ends when the participants run out of steam.
-
- Paul McJones mcjones@src.dec.com (editor of SRC 113)
-
- RTAllocator should allow handling out of memory
-
- David Goldberg: ... there is one system problem that is not currently
- handled, namely running out of memory. I would very, very much like to
- see this handled in RTAllocator. One approach was suggested by Roy
- Levin a while back: Have a RegisterNoMemory(proc) routine that causes
- proc() to be called when memory is gone (or very low). Example of use:
- in the 'Solitaire' program, the 'Hint' button generates a tree of
- possible moves. If this tree gets very big and consumes all memory,
- the RegisterNoMemory proc could abandon the search down the current
- branch, NIL-out that branch, and ask for a garbage collection.
- Currently what happens is that Solitaire crashes if you bug 'Hint' and
- memory is low.
-
- Interface Police: Ok, make a concrete proposal and we'll talk. How low
- should memory be before the runtime complains? Before or after a
- collection? Is it ok to call your procedure from inside a runtime
- critical section (after all, you're probably in the middle of a NEW)?
- Are multiple notification procedures allowed to be registered?
- Shouldn't a routine that consumes arbitrary amounts of memory be coded
- to poll the allocator to ask how much memory is available?
-
- Hans Boehm/Alan Demers/David Goldberg/David Nichols: We believe that
- programs wishing to handle allocation failures will be able to do so
- with high (but not perfect) reliability if the interface provides two
- features: versions of the RTAllocator.New routines that report if the
- allocation is not possible (either by returning NIL or raising an
- exception), and a way to register a callback when memory is low. Both
- features are necessary. Here are two typical scenarios:
-
- * The Solitaire program. Before starting, Solitaire allocates a
- 'safety net' block of memory, and registers a callback. When
- memory is exhausted, the callback frees the safety net, sets a
- flag, and returns. In the Solitaire program proper, the inner loop
- of the move generator checks the flag immediately after allocating
- a new node. If the flag is set, it abandons the search. It would
- not work for Solitiare to allocate new tree nodes with
- RTAllocator.New() and check for an error: as memory gets low, a
- library routine in some other package could cause an allocation
- failure.
- Unfortunately, there is a race condition since another thread
- could run and do an allocation between the time the faulting NEW
- returns and all references to the search tree are NIL'ed. This can
- be mimimized by adding some slop to the safety net.
- * An editor that allocates O(n) bytes of memory when opening an
- n-byte file. If the users tries to open a huge file, you don't
- want to crash, but rather tell the user that the file can't be
- opened (in UNIX, the user can then kill some processes to regain
- swap space and try again, or in an emacs-style editor he can
- delete some buffers and try again). A callback won't work for
- this, because when attempting to open a huge file, the allocation
- must be aborted: there just isn't enough memory to go around.
- Instead an RTAllocator.New() routine should be used for this
- allocation.
- However, the editor will also want to register a callback proc to
- guard against NEW()s in other parts of the program that can't be
- satisfied. If the callback is passed the size of the memory
- allocation that can't be satisfied, the callback will be able to
- pick between two strategies. If there is a 'safety net' which is
- larger than the block to be allocated, the callback can free it
- and set a "low on memory" flag, with the editor cleaning up
- properly later. If the safety net is not big enough, the callback
- itself can attempt an emergency save before crashing.
-
- Here's a specific proposal that embodies these ideas. We're not wedded
- to the details. Note that RTCollector.LimitRelative is not essential:
- it just lifts some of the functionality currently in RTHeapPolicy.
-
- * Add the following to RTCollector.i3:
- PROCEDURE LimitAbsolute(n: CARDINAL);
-
- (* Don't let the heap grow beyond n bytes. The collector/allocator
- should observe this in all heap growth decisions. *)
-
- [Comment from Hans: I don't think there is a way to write
- programs that are reasonable citizens on a shared system without
- some such facility.]
-
- PROCEDURE LimitRelative(x: REAL);
-
- (* Advisory. Try to keep the heap size at roughly x times the
- amount of live data. (For ref counting it affects only the
- backup collector for cycles.) *)
-
- [Comments from Hans: The performance of all collectors with
- which I am familiar depends crucially on such a parameter. Thus
- it might as well be exposed in some portable interface. (The
- allocator should of course use less memory if it gains no time
- advantage from using more.) The "amount of live data" is, of
- course, implementation defined, as are the minimum values of x
- that have any chance of being observed.]
- * In RTAllocator.i3, add OutOfMemory to RAISES clauses of all the
- New routines, and add the following:
- EXCEPTION OutOfMemory;
-
- TYPE
- CallBackObj = OBJECT notify(bytes: CARDINAL) END;
-
- PROCEDURE RegisterHeapFullCallback(obj: CallBackObj);
-
- (* Add obj.notify to the list of procs to be called if an
- allocation request is about to fail, either because of lack
- of memory, or due to violation of an
- RTCollector.LimitAbsolute imposed limit. The notify method
- will be called with an argument specifying the size in bytes
- of the allocate call that triggered the callback. The notify
- method may not allocate or acquire locks, or call any
- procedures that do. It may be invoked synchronously with any
- combination of locks held. (Should there be a way to delete
- a registered callback?). If a garbage collection after this
- callback fails to reclaim enough memory to allocate the
- requested object, an exception will be raised if the
- allocation was through RTAllocator. Otherwise a checked
- runtime error will result. The notify proc is not called
- when memory fails from an RTAllocator.New call (these
- failures can be caught by the user).
-
- Typical actions by notify would include one of the following:
-
- 1) Clearing pointers to reduce the amount of accessible memory.
- 2) Calling RTCollector.LimitAbsolute with a larger limit.
- *)
- * Variations on this proposal:
- Might want to consider adding:
- PROCEDURE GetLimitAbsolute(): CARDINAL;
- (* Return the current absolute heap limit *)
- The usefulness of RTCollector.LimitAbsolute in the callback would
- be increased if there was a way to tell if this actually freed up
- any more memory. One approach would be to change CallBackObj to
- TYPE
- CallBackObj = OBJECT
- notify(bytes: CARDINAL; retry: BOOLEAN): BOOLEAN
- END;
- and change the action of RegisterHeapFullCallback to:
- (* If a garbage collection after all callbacks have been
- executed fails to reclaim enough memory to allocate the
- requested object, then any notify() procs that returned TRUE
- will be called again with retry := TRUE. Otherwise an
- exception will be raised if the allocation was through
- RTAllocator, or else a checked runtime error will result. *)
- Thus, if you wanted to first try and get more memory in the
- callback by calling RTCollector.LimitAbsolute, you could return
- TRUE and wait for a callback with retry = TRUE. If this second
- callback occurs, you will need to clear some pointers to free up
- memory. Or another variation: add
- PROCEDURE GetTotalBytesAllocated(): CARDINAL;
- (* Returns the total number of bytes allocated since the program
- begin. A CARDINAL may not be big enough, perhaps this should
- be a LONGREAL? *)
- Then the retry argument to the notify method can be eliminated,
- since a call is a retry only if GetTotalBytesAllocated() shows no
- additional allocations since the last callback.
-
- John DeTreville: When I read your March proposal for handling running
- out of memory on the traced heap, I didn't quite see how to implement
- the details you gave. I've been iterating to create mechanisms that
- are simpler and more implementable, and I've now arrived at quite a
- simple interface.
-
- In particular, I now believe that (almost) all the functionality you
- ask for is already provided by the current interface. I say "almost"
- because there's a few status calls to be added, and because some of
- the current mechanisms are clunky, but I believe I can tell a
- convincing story. Note that these mechanisms are or would be in
- SRC-specific interfaces (currently called RTAllocatorSRC,
- RTCollectorSRC, and RTHeapRep); I don't think we understand them well
- enough to put them into the public IP interfaces.
-
- Let's first distinguish VM limits from application-imposed limits. The
- amount of VM available to the application is a hard limit, although
- not one that can easily be predicted. In the current SRC M3
- implementation, both the allocator and the collector allocate VM from
- the kernel when necessary. If the collector tries to allocate VM and
- fails, the program must crash: there is no way to reestablish the
- necessary invariants to let it continue.
-
- I propose treating VM exhaustion as a checked runtime error, in the
- allocator and in the collector. The goal is then to establish and
- maintain an application-imposed limit that is uniformly stricter than
- the VM limit, whatever that may be.
-
- You propose a mechanism to allow calls to the New* procedures to fail
- if they would exceed the application-imposed limit. Of course, only a
- small part of the code would take advantage of this facility. This
- code could equally well query the heap to determine the current size,
- and compare it against the limit; if the program can also predict the
- size of the object to be allocated, it can decide whether or not to
- proceed.
-
- This approach requires some collector-dependent code in the
- application, but I doubt that it would be very much. It also allows
- possible race conditions, but I believe they're not much worse in
- practice than in the original proposal.
-
- You also propose a mechanism to notify the program whenever the limit
- is about to be exceeded. It's quite complicated to get such immediate
- notification. First, the procedures notified can't acquire locks or
- call most procedures in other modules. Second, it requires a new
- collection to run synchronously after the procedures to see if enough
- space has been freed and whether some of the procedures must be called
- again; this causes an interruption of service.
-
- Here's a different proposal, which might not allow space bounds as
- tight as in the original proposal, but which seems simpler. We would
- add a mechanism for an application thread to wait for the next
- collection to finish. This mechanism could replace the current
- mechanisms for registering and unregistering synchronous monitors,
- which have numerous complex and poorly documented constraints on what
- actions they can perform.
-
- Each time through, the thread could compare the amount of space still
- reachable to the application-imposed limit, and either free some data
- before the next collection (the ability to hold locks would be handy
- here) or increase "gcRatio" to make the collector work harder and keep
- the total heap size under control, or both.
-
- There is still the danger that the application could allocate so
- rapidly that this asynchronous thread might not be able to keep up,
- but otherwise asynchronous actions seem a lot more reasonable than
- synchronous.
-
- This is one approach, and there are others. What's nice about this
- design is that it requires almost no changes to the interface, only
- better status reporting and a replacement of the mechanisms for
- registering and unregistering synchronous collection monitors. Maybe
- you could even work around the current lack of these facilities. Let
- me know what you think.
-
- Hans: One quick comment, without having thought much about the rest:
-
- "This code could equally well query the heap to determine the current
- size, and compare it against the limit; if the program can also
- predict the size of the object to be allocated, it can decide whether
- or not to proceed."
-
- Is this really true? Since the collector can't move some objects,
- there are presumably fragmentation issues. Am I guaranteed to be able
- to allocate 1 MB if the current heap size is 1 MB below the limit?
- This is certainly false in PCR, and I'm not sure how you could
- guarantee it without remapping pages.
-
- John: Hans Boehm notes that I was wrong about the client of New* being
- able to predict whether an allocation would succeed or fail, because
- of likely page-level fragmentation. This needs to be fixed in my
- proposal.
-
- To expand on my earlier message, let me outline a completely different
- approach for handling heap overflow, that perhaps has more in common
- with the original PARC proposal, but which seems far too complex and
- unwieldy to me. This complexity is why I tried to work out a simpler
- approach, even at the cost of providing fewer guarantees.
-
- We start by imagining that we want to be able to continue to run even
- if we exhaust VM. First, this means that we can never allocate VM from
- inside the collector. The implication is that whenever we allocate VM
- in the allocator, we allocate enough extra to tide us over through the
- next collection, no matter how much of the heap it retains. This
- suggests that we will significantly overallocate VM. For example, with
- a stop-and-copy collector and gcRatio set to 1, a program with a
- stably-sized reachable set currently requires 3 times as much space as
- the reachable set, but the "failsafe gc" would require 4 times.
-
- (Doing even this well depends crucially upon the SRC implementation
- detail that the current collector never copies objects bigger than one
- page, but leaves them in place. Otherwise, the possibility of
- fragmentation would make it much more difficult to determine how much
- memory to leave free for the collector, and in what sizes of runs of
- pages. It will also take some work to avoid off-by-one errors in
- predicting how much memory a collection could take.)
-
- Of course, if the client decreases gcRatio, or switches from
- sort-and-copy collection to concurrent collection, that would require
- allocating more VM, to ensure that the collector cannot run out of VM.
- That means that these operations can also fail, just like allocator
- operations.
-
- Only some programs will want to be able to back off when they reach VM
- limits. Others won't mind getting a checked runtime error; in return,
- they will require less VM. Therefore, we need procedures to switch
- back and forth between these two modes. Again, attempting to switch to
- failsafe mode can fail.
-
- The collector currently allocates its own space on the traced heap
- during collections, which will have to be moved to the untraced heap
- if we are to predict how much traced heap a collection can use. Note
- that in general, once VM is exhausted, allocations on the untraced
- heap may start to fail, and so programs will probably die very quickly
- once VM is exhausted. But let's move on.
-
- In addition to the VM limit, we also want an application-imposed limit
- on heap size. The allocator and collector will guarantee that the heap
- size will never exceed this limit. Again, we will overallocate VM in
- the allocator to avoid exceeding the limit in the collector. Again,
- setting the limit may fail.
-
- So what happens when a NEW fails, or a New*, or switching to
- concurrent collection, or setting the application-imposed limit, or
- whatever? This happens whenever performing the operation would exceed
- the application-imposed limit, or when attempts to allocate enough
- extra VM fail.
-
- Some of these can signal an error, and the client can chose to do
- something else instead. In some cases, such as setting gcRatio, it
- might make sense for the failing operation to tell the client how
- close to the impossible value would be possible.
-
- NEW, though, should not signal an error; this would require massive
- changes in all existing modules that would not be add value to most
- clients. In this case, I can't think of anything much better than the
- original proposal. Before attempting to allocate the object, the
- collector will try to free up some storage. First, it can perform a
- collection, or finish the current one. If that doesn't do it, it can
- call one or more procedures registered by the application to drop
- links to some storage, or to change collector parameters. If that
- doesn't do it, we can perform another collection. And so on and so on,
- until the procedures say to give up.
-
- Note that these collections must be synchronous, since no allocations
- may be possible until this mechanism completes, and the collections
- will therefore cause interruptions of service. Note also that the
- procedures cannot acquire locks, cannot allocate storage, cannot call
- thread primitives, and so on, and therefore cannot call into most
- libraries; they are essentially restricted to reading and writing data
- structures whose implementations they know, and changing collector
- parameters. This seems excessively restrictive, but also unavoidable
- in this approach.
-
- In short, this seems like a lot of extra mechanism to add to the
- allocator and collector, that doesn't seem to do quite what you want;
- it gives you strict limits, but at a cost. My proposal of this morning
- is at least much simpler, although it can give looser limits.
-
- John, continuing: Thinking a little more about the problem of running
- out of storage in the untraced heap, it seems that the only reasonable
- thing to do is to merge the implementation of the untraced heap with
- the traced heap. This was, untraced NEWs that fail can be handled
- exactly the same way as traced NEWs, with a synchronous cleanup
- routine that frees enough VM to proceed, or resets parameters.
-
- This means that the allocator and collector cannot use the untraced
- heap, but must either use a static amount of storage which they could
- overflow, or must allocate enough extra in response to client
- applications that they cannot possibly run out of space. The potential
- space overhead for maximum-sized stacks, for example, is huge.
-
- The more this proposal is fleshed out, it more it seems that doing a
- good job of recovering from heap overflows is quite tricky, which is
- why I suggest a lower-tech approach for now.
-
- Hans: I just went over the last few messages in this thread again. I
- think the bottom line is, as you say:
-
- It's hard to implement an out-of-memory call-back on top of the
- current collector. Given the current collector, a collector call-back
- that allows polling is probably the best you can reasonably do, and
- should certainly be provided.
-
- The remaining question, which also seems to be motivated by other
- concerns here, is: To what extent are you tied to this collector
- design? The problem here seems to be mainly caused by copying old
- generation objects, since you could perhaps bound the size of the
- young generation? My suspicion, based unfortunately only on anecdotes,
- is that this is not a good idea anyway, since it uses too much space,
- and is also fairly expensive in copying cost. (PARCPlace seems to have
- arrived at the same conclusion, so there's probably at least one other
- supporter of this position. Some recent complaints here about space
- usage of Modula-3 programs also point a bit in this direction.)
-
- Do you agree? If so, should the interface be designed ignoring current
- constraints, and should we initially accept a partial implementation?
-
- John: It's been a while, so let me recap where we are, or at least
- where I am.
-
- We've been discussing mechanisms for Modula-3 programs to manage their
- memory better. In particular, we have proposed ways that programs
- could bound the heap size and recover from heap overflow.
-
- I think this topic is complicated enough, and new enough, that we
- shouldn't try to get it into the current set of portable interfaces.
- The Interface Police concur.
-
- We've floated two broad (families of) proposals for attacking this
- problem:
-
- * Allow strong guarantees on the heap size; these guarantees would
- never be broken.
- * Allow the program to monitor its memory usage, discarding excess
- data as necessary.
-
- I think that the first is achievable. Adapting the current SRC
- Modula-3 (allocator and) collector to allow such guarantees would take
- a month or so. The principal problem is that the current collector
- tries to maximize space/time performance, and giving such guarantees
- will probably require extra memory to be set aside that will never be
- used. The collector would have two modes: with or without guarantees.
- Most programs would run without guarantees.
-
- I also think that a usable version of the second is possible with
- almost no change to the current collector. The programmer would have
- to do more work, and wouldn't get any strong guarantees, but this
- approach should work for many programs.
-
- We've also been discussing a third family of proposals, that seem to
- combine the worst features of the first and second: they require
- significant changes to the current collector, but son't give very
- strong guarantees. These seem much less interesting to me.
-
- Here's two pieces of opinion.
-
- First, I propose that we work out the details of #2, and you use it
- for a couple of programs. Get some experience with it. This could help
- inform a heavier-weight solution.
-
- Second, I wonder whether any of these solutions is a good candidate
- for a portable interface. It's one thing not to be strictly
- incompatible with a given collector strategy, but quite another to be
- easy to plug into an existing collector. Modula-3 currently doesn't
- require very much from its collector; making these proposals standard
- would significantly increase the requirements on a Modula-3
- implementor.
-
- Why uppercase keywords?
-
- Some people prefer uppercase keywords others hate them. Another
- possibility is to accept both forms for keywords. This topic has been
- discussed at length and there is no solution that will completely
- satisfy everyone's tastes. Fortunately this is a very minor issue and
- you can easily have lowercase keywords automatically converted for you
- using an emacs macro package like [26]m3su .
-
- Why CONST Comments in Variables Declarations?
-
- John Kominek (kominek@links.uwaterloo.ca) wrote: Sprinkled throughout
- SRC m3 you'll find "constant" variables exported in interfaces. For
- instance,
-
- VAR (*CONST*) Grain: LONGREAL;
-
- where Grain is assigned during module initialization. Instead, did the
- modula-3 designers consider doing this.
- READONLY Grain: LONGREAL;
-
- Here the keyword permits only exporting modules to modify the Grain
- variable. Is there a problem with this proposal? The READONLY keyword
- is successfully used at procedure boundaries, so why not also at
- interface boundaries?
-
- Bill Kalsow replies:
-
- A problem with this proposal is that any module can claim to export
- the interface containing the variable, hence any module could modify
- the variable. Note that CONST says more than just READONLY. CONST
- implies that the variable should not be modified by clients and that
- once it is initialized, it won't be changed later by the
- implementation. READONLY would only mean that clients should not
- modify the variable. IMO, the "right" solution would have been to
- allow:
- INTERFACE Foo;
- CONST x: T;
-
- MODULE Foo;
- CONST x: T := <value>;
-
- In the same way it checks revelations for opaque types, the compiler
- could check that only one module provided a value for the constant.
- But, this proposal doesn't quite hang together either. Consider this
- example:
- CONST x: INTEGER;
- VAR v: [0..x];
-
- The language definition says that "v"s definition is illegal if "x <
- 0" because its type is "empty". The system could refuse to run the
- program by delaying the check until it had seem the corresponding
- implementation module. But, I think you'll agree that it could quickly
- turn into a mess. The most flexible handling of opacity I've seen is
- in Christian Collberg's PhD Thesis, "Flexible Encapsulation". It was
- published Dec 5, 1992 by the CS Dept at Lund University, Lund Sweden.
- If I remember correctly, his system was capable of deferring all
- checks and decisions imposed by opaque declarations until link time.
-
- References
-
- 1. mailto:michel.dagenais@polymt.ca
- 2. http://m3.polymtl.ca/m3
- 3. http://www.cmass.com/cm3/projects.html
- 4. http://www.cmass.com/
- 5. http://www.elego-software-solutions.com/
- 6. http://www.m3.org/cm3/
- 7. file://localhost/home/m3/tmp/m3/pm3/intro/src/concise-bib.html
- 8. file://localhost/home/m3/tmp/m3/pm3/intro/src/bib.html
- 9. file://localhost/home/m3/tmp/m3/pm3/intro/src/bib.html#SPwM3
- 10. file://localhost/home/m3/tmp/m3/pm3/intro/src/bib.html#m3-Har92
- 11. http://www.m3.org/
- 12. http://www.research.digital.com/SRC/modula-3/html/home.html
- 13. http://m3.polymtl.ca/m3
- 14. ftp://ftp.cs.colorado.edu/pub/cs/techreports/zorn/CU-CS-641-93.ps.Z
- 15. http://www.research.digital.com/SRC/modula-3/html/home.html
- 16. http://www.cmass.com/
- 17. http://www.m3.org/cm3/
- 18. http://m3.polymtl.ca/m3
- 19. http://m3.polymtl.ca/
- 20. http://www.cs.washington.edu/research/projects/spin/www/
- 21. file://localhost/home/m3/tmp/m3/pm3/language/modula3/m3tools/m3gdb/src
- 22. file://localhost/home/m3/tmp/m3/pm3/text/sgmltools/sgml/src/nsgmls
- 23. mailto:rrw1000@cus.cam.ac.uk
- 24. file://gatekeeper.dec.com/pub/DEC/Modula-3/
- 25. http://gatekeeper.dec.com/pub/misc/detlefs/escover.ps
- 26. ftp://pion.lcs.mit.edu/pub/m3su
- --
-
- Prof. Michel Dagenais http://m3.polymtl.ca/dagenais
- DΘpartement de gΘnie informatique michel.dagenais@polymtl.ca
- Ecole Polytechnique de MontrΘal tel: (514) 340-4711 ext.4029
-