Date: Fri, 1 Feb 91 15:04:14 PST From: muller@src.dec.com (Eric Muller) Subject: archive of comp.lang.modula3 for January 91 available The articles posted to comp.lang.modula3 during the month of January 91 are availabe via anonymous ftp on gatekeeper.dec.com, in 'pub/DEC/Modula-3/comp.lang.modula3/jan91.Z'. Eric. -- Eric. ------------------------------------------------------------------------------ Date: Mon, 4 Feb 91 09:36:37 EST From: saber.com!wyant@saber.com Subject: Typefinger printing in SRC M3 Can someone give a $.50 overview of the type fingerprinting algorithms used in SRC M3 ? My surface impression from scanning the sources is that works as follows. Each type (scalar or constructed) has an operation to return a textual type name. When fingerprinting a constructed type (esp. an object/record) a string is constructed containing the recursive enumeration of all scalar type names used in that constructed type. This string is then treated as input to the "poly" function to compute a 64-bit fingerprint. Is this reasonably accurate or am I way out in left field >> Thanks in advance :-) Geoff Wyant wyant@saber.com ------------------------------------------------------------------------------ Date: Tue, 5 Feb 91 14:59:26 PST From: Subject: Re: language change wishes / Olivetti Modula-3 info needed Marc Wachowitz suggests that "UNTRACED" should be a function from types to types; where "UNTRACED T" is like T, but not traced by the garbage collector. For example, TYPE T = OBJECT n: INTEGER END; UT = UNTRACED T; This would be useful if T was very long or defined in a different interface. Unfortunately, this doesn't work. Consider TYPE T = OBJECT METHODS p := P END; PROCEDURE P(self: T); TYPE U = UNTRACED T; What is the p method of U? It can't be P, since the type of the first argument is wrong. So it cannot be true that "UNTRACED T" is like T except that it is not traced by the garbage collector. When this simple definition failed, the committee gave up on the idea that UNTRACED was a function from types to types. ------------------------------------------------------------------------------ Date: 7 Feb 91 21:10:46 GMT From: new@ee.udel.edu (Darren New) Subject: Questions from a new advocate of Modula-3 Maybe somebody out there can answer a couple of questions for me about Mod-3, especially if you have a running version (since all I have is the report): (Note: I can FTP to gatekeeper, so a pointer to the appropriate month were this was discussed would be enough. Thanks!) 1) The type of an expression is the set of all values the expression can take on. By definition, a constant expression can only take on one value. So if I declare something as PROCEDURE x(y := 6); does this not imply that the type of y is [6..6]? What is the type really, and why is it INTEGER instead of CARDINAL (if it is)? Should the report not say something about "base type" instead in this case? 2) The NEW expression's behaviour is not defined in the case that not enough memory is left. Is this a checked runtime error, or does it return NIL? If it returns NIL, then it isn't returning a "new" value. If it is a checked runtime error, how do you allocate all available memory without crashing? 3) A new variable is initialized to one of the members of the type. Does this mean that (say) a UNTRACED REF REAL could be initialized to point to any REAL anywhere in memory, or only to a REAL that has been actually allocated via NEW? I.e., if REALs must be on 4-byte boundries, could a newly-declared but otherwise uninitialized UNTRACED REF REAL point to *any* 4-byte-aligned quantity, or to any in the untraced heap, or to any previously-initialized real, or what? Does an initialized nonNIL REF necessarily point to initialized data? 4) BIT n OF t <: t -- I can accept that t <: BIT n OF t -- What about BIT 2 OF [0 .. 255]? Is this a static error, a checked runtime error, or implementation dependant? 5) Why try for generic packages given that objects can support generic types (like the Queue and IntQueue example)? 6) I would think that REAL <: LONGREAL, but that doesn't appear to be the case. 7) Has an "assert" capability been considered? Maybe preconditions and postconditions could be included. It seems like this would make for a much "safer" situation where an unsafe module exports a safe interface. Asserts in interfaces could help document the semantics of the interface. ------------ Then some comments: REFs should be defined to be initialized to NIL (unless explicitly initialized). This would catch more errors because a pointer that missed being initialized could not point to valid data possibly being used by some other module/thread/whatever. This would also prevent code which assumed that pointer are initialized to NIL (probably the vast majority of cases) from failing when it isn't (Holy VAXisms, batman!). An interface should be defined which specifies the ranges of integers, floats, etc. (Like LIMITS.H in ANSI C) --- Darren New --- Grad Student --- CIS --- Univ. of Delaware --- ----- Network Protocols, Graphics, Programming Languages, Formal Description Techniques (esp. Estelle), Coffee, Amigas ----- =+=+=+ Let GROPE be an N-tuple where ... +=+=+= -- --- Darren New --- Grad Student --- CIS --- Univ. of Delaware --- ----- Network Protocols, Graphics, Programming Languages, Formal Description Techniques (esp. Estelle), Coffee, Amigas ----- =+=+=+ Let GROPE be an N-tuple where ... +=+=+= ------------------------------------------------------------------------------ Date: Thu, 7 Feb 91 20:00:50 PST From: Subject: Re: Questions from a new advocate of Modula-3 Here are answers to some questions from Darren New: 1) The type of an expression is the set of all values the expression can take on. By definition, a constant expression can only take on one value. So if I declare something as PROCEDURE x(y := 6); does this not imply that the type of y is [6..6]? What is the type really, and why is it INTEGER instead of CARDINAL (if it is)? Should the report not say something about "base type" instead in this case? The type of an expression CONTAINS all values the expression can take on, but it may contain other values as well. For example, if the type of n is INTEGER, the type of n - n is also INTEGER, not [0..0]. In general, to determine the type of an expression, you look in the expressions chapter. In particular, the type of an integer literal is defined to be INTEGER, even if the literal is positive. 2) The NEW expression's behaviour is not defined in the case that not enough memory is left. Is this a checked runtime error, or does it return NIL? If it returns NIL, then it isn't returning a "new" value. If it is a checked runtime error, how do you allocate all available memory without crashing? The report should have defined this to be a checked runtime error. If you want to allocate all available memory without crashing, you have to use a lower-level, implementation-dependent interface. 3) A new variable is initialized to one of the members of the type. Does this mean that (say) a UNTRACED REF REAL could be initialized to point to any REAL anywhere in memory, or only to a REAL that has been actually allocated via NEW? I.e., if REALs must be on 4-byte boundries, could a newly-declared but otherwise uninitialized UNTRACED REF REAL point to *any* 4-byte-aligned quantity, or to any in the untraced heap, or to any previously-initialized real, or what? Does an initialized nonNIL REF necessarily point to initialized data? In principle an UNTRACED REF REAL could be initialized to point to a REAL anywhere in memory. If the implementation requires real pointers to be byte-aligned, then the initial value would have to be byte-aligned. An initialized non-nil REF must point to valid data; but the data can be arbitrary; for example: VAR r := NEW(REF INTEGER) leaves r pointing to an arbitrary integer. 4) BIT n OF t <: t -- I can accept that t <: BIT n OF t -- What about BIT 2 OF [0 .. 255]? Is this a static error, a checked runtime error, or implementation dependant? The language definition gives the implementer complete freedom over which values of n are allowed in BITS n FOR T. Unless the implementer knows of a technique by which eight bits of information can be represented with two bits of storage, he would do best to prohibit BITS 2 FOR [0..255]. (If he knows such a technique, he should patent it.) 5) Why try for generic packages given that objects can support generic types (like the Queue and IntQueue example)? Generics can be more efficient: for example, a generic matrix package could be instantiated for either REAL or LONG REAL. In principle you could do this with objects: you just define an object type "number" with methods add, multiply, etc., and then define two subtypes, in one subtype the number object's data field is a REAL, in the other subtype the data field is a LONGREAL. But the overhead in this approach is horrendous. 6) I would think that REAL <: LONGREAL, but that doesn't appear to be the case. That would have been a possible design. It leads to a small thicket of design questions. For example, with VAR r: REAL and VAR lr: LONGREAL, the subtype rule that you definitly allows the assignment lr := r, which probably establishes the post-condition lr = r. Whether the reverse assignment is allowed is more questionable; and for the reverse assignment to establish the equality is getting very dubious. In the end we decided to avoid these questions by not including the rule. 7) Has an "assert" capability been considered? Maybe preconditions and postconditions could be included. It seems like this would make for a much "safer" situation where an unsafe module exports a safe interface. Asserts in interfaces could help document the semantics of the interface. SRC Modula-3 has an ASSERT pragma. REFs should be defined to be initialized to NIL (unless explicitly initialized). This would catch more errors because a pointer that missed being initialized could not point to valid data possibly being used by some other module/thread/whatever. This would also prevent code which assumed that pointer are initialized to NIL (probably the vast majority of cases) from failing when it isn't (Holy VAXisms, batman!). This is a point on which reasonable people can differ. I am skeptical that more errors would be caught if the language requires that REFs be initialized to NIL, since in fact, both existing implementations of M3 do initialize REFs to NIL. I find it most readable to supply the initial value if and only if it matters to the algorithm. Thus: VAR t: TEXT; BEGIN FOR i := 0 TO 9 DO IF P(i) OR i = 9 THEN t := Fmt.Int(i) END END; ... END But VAR a: IntList := NIL; BEGIN FOR i := 0 TO 9 DO a := Cons(i, a) END; ... END It is hard to recommend that programmers write the ":= NIL" if the language defines it to be a no-op. An interface should be defined which specifies the ranges of integers, floats, etc. (Like LIMITS.H in ANSI C) I hope the new required floating point interfaces answer your needs (see the "twelve changes to Modula-3" message last December on comp.lang.modula3). Greg Nelson ------------------------------------------------------------------------------ Date: 8 Feb 91 13:42:27 GMT From: bbc@rice.edu (Benjamin Chase) Subject: Re: Questions from a new advocate of Modula-3 gnelson (Greg Nelson) writes: >Here are answers to some questions from Darren New: > 2) The NEW expression's behaviour is not defined in the case that > not enough memory is left. Is this a checked runtime error, or > does it return NIL? If it returns NIL, then it isn't returning > a "new" value. If it is a checked runtime error, how do you > allocate all available memory without crashing? I think this is the wrong decision. Darren and the previous people asking this same question have a very good point. There should be a standard (ie. not implementation-dependent) way to safely allocate memory. It shouldn't have to blow your program out of the water to let you know that there was no available memory. There should be a way to get NEW to raise an exception. >The report should have defined this to be a checked runtime error. >If you want to allocate all available memory without crashing, you >have to use a lower-level, implementation-dependent interface. > 7) Has an "assert" capability been considered? Maybe preconditions > and postconditions could be included. It seems like this would > make for a much "safer" situation where an unsafe module exports > a safe interface. Asserts in interfaces could help document > the semantics of the interface. >SRC Modula-3 has an ASSERT pragma. Perhaps another required module instead of, or in addition to, an ASSERT pragma? Yeah, I think I know why the pragma; you want the compiler to evaluate the predicate if it can, and generate a compile-time "assertion-botched" message. This does have its selling points. People are more likely to use assertions if they feel that there is no run-time penalty. Sigh. ps: Greg, my mailer couldn't easily reply to your posting... -- Ben Chase , Rice University, Houston, Texas ------------------------------------------------------------------------------ Date: Fri, 8 Feb 91 12:17:23 PST From: muller@src.dec.com (Eric Muller) Subject: Re: Questions from a new advocate of Modula-3 In article , Benjamin Chase writes: > >SRC Modula-3 has an ASSERT pragma. > > People are more likely to use assertions if they feel that > there is no run-time penalty. The ASSERT pragma works like this: <* ASSERT boolean-expression *> Normally, this is the same as: IF NOT boolean-expression THEN RuntimeError ("Assert Failed"); END; If you compile your program with '-a', the pragma is ignored. -- Eric. ------------------------------------------------------------------------------ Date: 8 Feb 91 17:42:23 GMT From: doug@snitor.uucp (Doug Moen) Subject: running out of memory In article <9102080401.AA29237@jumbo.pa.dec.com> gnelson (Greg Nelson) writes: > 2) The NEW expression's behaviour is not defined in the case that > not enough memory is left. Is this a checked runtime error, or > does it return NIL? If it returns NIL, then it isn't returning > a "new" value. If it is a checked runtime error, how do you > allocate all available memory without crashing? > >The report should have defined this to be a checked runtime error. >If you want to allocate all available memory without crashing, you >have to use a lower-level, implementation-dependent interface. I strongly disagree. The report should have defined this to raise an exception. There are many programs for which crashing on memory exhaustion is unacceptable behaviour. Examples include: text editors, shells, window managers, daemons providing essential system services, and just about any interactive program. In order to write any of these programs in Modula-3, it is necessary to: - use a low-level, implementation-dependent interface for allocating memory (which means the program isn't portable) - avoid using any library module that calls NEW. This means avoiding most standard modules, including Text and Thread. These are very serious restrictions, which lead me to believe that Modula-3 is simply not suitable for writing interactive programs or daemons that are required to be robust. The text editor I use is written in C, and does not crash when it runs out of memory or temp file space. Instead, it displays an error message, and permits me to save my changes before exiting. This robustness has saved my bacon on at least one occasion. Last year, I wrote an image editor using Motif & the X Toolkit. This editor has prodigious memory requirements, since the images we deal with can be quite large. The X Toolkit aborts your program if it runs out of memory. As a result, it is possible to crash my editor simply by opening a large number of windows. Of course, when the editor crashes, you lose all of your work. This has actually happened to users of my program. I find this most upsetting, since to fix the bug, I would have to reimplement Motif and the X Toolkit. The preface to the Modula-3 report says: "The goal of Modula-3 is to be as simple and safe as it can be while meeting the needs of modern systems programmers." Modula-3 can't meet these goals until NEW has been redefined to raise an exception on memory exhaustion. ------------------------------------------------------------------------------ Date: Mon, 11 Feb 91 10:38:39 EST From: hudson@yough.ucc.umass.edu (Rick Hudson) Subject: Typefinger printing in SRC M3 My surface impression from scanning the sources is that works as follows. Each type (scalar or constructed) has an operation to return a textual type name. When fingerprinting a constructed type (esp. an object/record) a string is constructed containing the recursive enumeration of all scalar type names used in that constructed type. This string is then treated as input to the "poly" function to compute a 64-bit fingerprint. Is this reasonably accurate or am I way out in left field >> This was my impression of how SRC M3 works also. It is a reasonable probabilistic approach to the problem. A non-probabilistic approach would be to use the fingerprint only for determining if types were *not* equivalent and actually compare structures before deciding if they were equivalent. - Rick ------------------------------------------------------------------------------ Date: Mon, 11 Feb 91 10:57:44 EST From: moss%ibis@cs.umass.edu (Eliot Moss) Subject: checked run-time errors and exceptions (was: running out of memory) >>>>> On 8 Feb 91 17:42:23 GMT, doug@snitor said: doug> The preface to the Modula-3 report says: doug> "The goal of Modula-3 is to be as simple and safe as it can be doug> while meeting the needs of modern systems programmers." doug> Modula-3 can't meet these goals until NEW has been redefined doug> to raise an exception on memory exhaustion. I tend to agree with this, and NEW is not the only problem. I think the language committee should revisit the issue of checked run-time errors versus exceptions with an eye to supporting the construction of very robust programs: ones that attempt to handle errors on their own and continue operation. Running out of storage in NEW is an obvious case, but there are undoubtedly others. I would tend to argue that even though accessing beyond the bounds of an array is an error (i.e., should never happen in a properly coded program), it may still be reasonable for a robust program to attempt to recover from bugs in some of its routines. Along these lines, CLU defined a number of specific exceptions for various situations, including "bounds", "end_of_file", and the like, and had a (slightly special) exception called "failure" for indicating that the underlying support abstraction(s) had failed. Running out of storage was one failure situation; accessing an uninitialized variable was (supposed to be) another (not fully implemented to my knowledge, but more modern compiler technology makes it easier); de-referencing a NIL pointer is a good example in Modula-3. A robust program could be built that would ty to clean up as much as possible from a failure exception and abort or skip the particular task it was working on at the time, perhaps logging the failure for maintainers (though log overflow should also be considered!). Such a program needs to be a bit intelligent about whether an error is recoverable (and the action should be restarted) or not (in which case the action should *not* be restarted, to avoid an infinite loop; at the very least, the actio should be deferred if at all possible). This means that the exceptions need to be standardized enough for programs to be able to tell the difference. All of this needs to be standardized as much as possible, for portability, though there are bound to be OS-, compiler-, and application-specific things that cannot be dealt with in the language definition. J. Eliot B. Moss, Assistant Professor Department of Computer and Information Science Lederle Graduate Research Center University of Massachusetts Amherst, MA 01003 (413) 545-4206, 545-1249 (fax); Moss@cs.umass.edu ------------------------------------------------------------------------------ Date: Mon, 11 Feb 91 11:57:57 -0800 From: harbison@bert.pinecreek.com (Sam Harbison) Subject: Re: checked run-time errors and exceptions I also think all checked run-time errors should be mapped to exceptions, for reasons already presented. I think there's value in NOT changing the language any more, but this issue is close to my "must fix" threshold. If the language committee prefers not to make the change, I would be in favor of proposing a model of errors-as-exceptions that "politically correct" implementations of Modula-3 would be encouraged to follow, as permitted but not required by the language spec. Sam Harbison Pine Creek Software, Pittsburgh, PA. +1 412 681 9811 ------------------------------------------------------------------------------ Date: 11 Feb 91 14:02:11 GMT From: emery@aries.mitre.org (David Emery) Subject: Re: running out of memory Ada provides an exception (STORAGE_ERROR) that is raised when the allocator cannot return any memory. I have likened this to a parachute that opens on impact, in that it meets its absolute requirements but is unusable in most practical situations. It is not enough to have an exception, you also have to define what can be done to handle the exception. In the case of Ada, STORAGE_ERROR can be raised in (at least) 2 circumstances: 1. out of heap 2. out of stack There is no good way to tell the difference between these two occurances. Furthermore, if you've run out of stack, you probably can't do anything at this point, because anything interesting (such as calling a cleanup routine) would require more stack space. My point here is that if you want to be able to recover from storage exhaustion, you need more than notification. You also need some understanding of what is permissible and not permissible after the exception has been raised. Finally, you'd like very much to have this defined in such a way that your program is portable (i.e. maybe the language should define a minimum permissible set of operations/actions, which can be extended by an implementation.) dave emery ------------------------------------------------------------------------------ Date: 11 Feb 91 16:43:00 GMT From: new@ee.udel.edu (Darren New) Subject: Recommended interfaces -- a suggestion I see many places in the report where it says things like "the implementation may define this runtime error as an exception." I would like to suggest that the standard-setters adopt a policy somewhat like the required/recommended status of the Internet RFCs. That is, why not define an interface in which runtime errors (or running out of memory during NEW :0) which are defined as exceptions are declared. Give them standard names. Errors which cannot be caught this way would not be declared in this interface (or would be commented out), but if they are caught, they must be declared and must have the standard name. Exceptions could include things like IntegerDivByZero, OutOfMemory, OutOfResource, UserKeyboardInterrupt, SystemShutdownInterrupt, and so on. Some such exceptions would probably be included implicitly in all RAISES clauses, while others could be listed explicitly. This way, relying on being able to catch certain kinds of errors (Out Of Memory, say) which may not be catchable would be caught at compile time when the referenced exception is not declared in the interface module. This could prevent the problems that C has with 18gazillion different sets of SIGNALs, depending on which O/S and compiler you are using. -- Darren -- --- Darren New --- Grad Student --- CIS --- Univ. of Delaware --- ----- Network Protocols, Graphics, Programming Languages, Formal Description Techniques (esp. Estelle), Coffee, Amigas ----- =+=+=+ Let GROPE be an N-tuple where ... +=+=+= ------------------------------------------------------------------------------ Date: 11 Feb 91 14:47:34 GMT From: rminnich@super.ORG (Ronald G Minnich) Subject: Re: running out of memory In article doug@snitor.uucp (Doug Moen) writes: >In article <9102080401.AA29237@jumbo.pa.dec.com> gnelson (Greg Nelson) writes: >> ... >>The report should have defined this to be a checked runtime error. >>If you want to allocate all available memory without crashing, you >>have to use a lower-level, implementation-dependent interface. >I strongly disagree. The report should have defined this to raise >an exception. It is worth remembering that 13 or so years ago the choice was between a real low-level language (C) and a nicer language (from many points of view) Pascal. It is amazing how similar the arguments are back then and now between, e.g., Mod 3 and C++. Some of them are identical (initialization, for example)! Problem was, Pascal tended to blow your program out of the water on failed file opens, failed memory allocs, and so on. While Pascal was nice from many points of view, its unrealistic model for programming (e.g. if you can't alloc, die) rendered it useless for most people, and, sad to say, C won. I hope the same mistakes are not repeated in Modula 3. Failed memory allocation should be an exception. Just about anything that can go wrong should be able to be handled by the program. ron -- "Socialism is the road from capitalism to communism, but we never promised to feed you on the way!"-- old Russian saying "Socialism is the torturous road from capitalism to capitalism" -- new Russian saying (Wash. Post 9/16) ------------------------------------------------------------------------------ Date: Mon, 11 Feb 91 15:28:50 PST From: mjordan@src.dec.com (Mick Jordan) Subject: Re: checked run-time errors and exceptions (was: running out of memory ) In article <9102111557.AA02459@ibis.cs.umass.edu>, moss%ibis@cs.umass.edu (Elio t Moss) writes: > I tend to agree with this, and NEW is not the only problem. I think the > language committee should revisit the issue of checked run-time errors versus > exceptions with an eye to supporting the construction of very robust programs : > ones that attempt to handle errors on their own and continue operation. > Running out of storage in NEW is an obvious case, but there are undoubtedly > others. I would tend to argue that even though accessing beyond the bounds of > an array is an error (i.e., should never happen in a properly coded program), > it may still be reasonable for a robust program to attempt to recover from > bugs in some of its routines. > If my memory is correct, two committee members including myself, argued strongl y for checked run-time errors to be mapped in the "fail" exception, which would be implicitly included in every RAISES clauses. This is the situation in Modula-2+, which even names this exception as FAIL. On can catch FAIL with an ELSE handler which, when used with restraint, provides adequate last ditch bullet-proofing. The argument against boils down to implementation difficulties in mapping some hardware failures (efficiently) into exceptions. (Having said that, the Olivetti implementation did map failures into an exception, which could be caught by ELSE. We used this to good effect in one program which, when it crashed, would bring down the entire OS). However, I have a strong dislike for naming individual failure exceptions and, therefore, encouraging, for example, code which catches NIL faults. There is something very bizarre about explicitly catching what are truly programming errors. Although running out of memory is not a programming error, it is effectively a failure of the Modula-3 abstract machine, and I am curious to know how you can write portable code to recover from it, given that you must be very careful not to call any code which might call NEW again. Mick Jordan ------------------------------------------------------------------------------ Date: Mon, 11 Feb 91 16:58:51 PST From: Subject: Re: running out of memory Doug Moen proposes that Modula-3 define an exception to be raised by NEW when storage is exhausted, arguing that this is necessary in order to write robust programs, such as a text editor that saves edits when storage is exhausted. Eliot Moss and Mick Jordan point out that if this approach is used to provide robust programs, it should probably be extended to many other checked runtime errors, perhaps to all of them. With this approach, the main body of, say, a robust text-editor would use a TRY-EXCEPT: TRY
EXCEPT RunTimeError(ErrCode) => Wr.PrintText(Stdio.stderr, CodeToMessage(errCode)); Wr.PrintText(Stdio.stderr, "Saving edits..."); END Of course, as Dave Emery points out, has to be coded very carefully (for example, it must not allocate any storage). In fact, Wr.PrintText might allocate storage, so the TRY-EXCEPT-ELSE code above is useless as it stands. But we can imagine providing a very robust library procedure that prints a TEXT on Stdio.stderr without allocating any storage or depending on anything except the very lowest levels of the runtime system, and use it instead of Wr.PrintText. A more serious problem with the approach is that Modula-3 programs are multi-threaded. The TRY--EXCEPT--END construct above has to be wrapped around every thread, not just the main thread, since storage exhaustion could happen anywhere in the program. The programmer writing the text editor can wrap the TRY--EXCEPT--END around all the threads forked directly by his program, although he might complain that this is awkward. But even this is not enough, since library packages fork background worker threads. For example, the Trestle window system forks two threads to buffer the queue of events from the X server. The Trestle implementation can hardly be expected to wrap the TRY--EXCEPT--END code around the threads that it forks, since the action is entirely foreign to Trestle. The solution to these problems is to provide an interface through which clients can register procedures to be executed when a program aborts due to a checked runtime error. A text editor could register a procedure that saves the edits. An elevator control program could provide a procedure that stops the elevator by the most primitive mechanism available. A program development environment could enter the debugger. The design of this interface is quite system-dependent, as these examples suggest, but it should not be difficult to provide a single interface that works in, say, all standard Unix environments. SRC Modula-3 does not yet provide such an interface, but Eric Muller tells me that one is on the way. Greg Nelson ------------------------------------------------------------------------------ Date: Mon, 11 Feb 91 18:07:50 PST From: muller@src.dec.com (Eric Muller) Subject: Re: running out of memory In article <9102120059.AA15796@jumbo.pa.dec.com>, gnelson (Greg Nelson) writes: > Of course, as Dave Emery points out, has to be coded very > carefully (for example, it must not allocate any storage). In fact, > Wr.PrintText might allocate storage, so the TRY-EXCEPT-ELSE code above > is useless as it stands. But we can imagine providing a very robust > library procedure that prints a TEXT on Stdio.stderr without allocating > any storage or depending on anything except the very lowest levels > of the runtime system, and use it instead of Wr.PrintText. The SmallIO interface in the core library will provide that functionality in SRC Modula-3 1.6. > The design of this interface is quite system-dependent, > as these examples suggest, but it should not be difficult to provide > a single interface that works in, say, all standard Unix environments. > SRC Modula-3 does not yet provide such an interface, but Eric Muller > tells me that one is on the way. New in RTMisc.i3: TYPE Exitor <: REFANY; PROCEDURE RegisterExitor (p: PROCEDURE (n: INTEGER)): Exitor; (* Registers the procedure p to be executed when Exit is called or when a checked runtime error is dectected; it is passed the argument of Exit, -1 for checked runtime errors. The registered procedures are executed in the reverse order of registration. *) PROCEDURE UnregisterExitor (e: Exitor); (* removes e's procedure from the registered set. *) PROCEDURE Exit (n: INTEGER); (* call the registered exitors and terminate the program with status 'n' *) -- Eric. ------------------------------------------------------------------------------ Date: Tue, 12 Feb 91 14:50:14 GMT From: weikart@prl.dec.com (Chris Weikart) Subject: Re: running out of memory In article <42601@super.ORG> rminnich@super.ORG (Ronald G Minnich) writes: > It is worth remembering that 13 or so years ago the choice was > between a real low-level language (C) and a nicer language (from many > points of view) Pascal. It is amazing how similar the arguments > are back then and now between, e.g., Mod 3 and C++. Some of them > are identical (initialization, for example)! > > Problem was, Pascal tended to blow your > program out of the water on failed file opens, failed memory allocs, > and so on. While Pascal was nice from many points of view, its > unrealistic model for programming (e.g. if you can't alloc, die) > rendered it useless for most people, and, sad to say, C won. It wasn't so simple. Pascal "lost" partly because of various rigidities, but mostly because of U*ix. ------------------------------------------------------------------------------ Date: Tue, 12 Feb 91 11:23:32 PST From: mjordan@src.dec.com (Mick Jordan) Subject: Re: RTMisc.i3 I think it would be better to register an object type, cf Thread.Closure, so that relevant state can be available. Plus, "normal" exit, i.e. without an explicit call of Exit, should be defined to call Exit(0), so I can routinely use it for tidy-up in all cases (its just too easy forget to code the call). And in that case RTMisc is an awful name for such an important interface. Why not call it RTExit, and use {Register, Cancel, Apply}? Mick Jordan ------------------------------------------------------------------------------ Date: 12 Feb 91 20:19:55 GMT From: new@ee.udel.edu (Darren New) Subject: Re: checked run-time errors and exceptions (was: running out of memory ) In article <1991Feb11.152850.1851@src.dec.com> mjordan@src.dec.com (Mick Jordan ) writes: >Although running out of memory is not a programming error, it is >effectively a failure of the Modula-3 abstract machine, and I am curious to >know how you can write portable code to recover from it, given that you must >be very careful not to call any code which might call NEW again. When I've needed to do such a thing, I've allocated space at the start of execution, and when malloc() returned NULL, I freed that space and cleaned up. Hence, the cleanup code could reuse that space to recover. Of course, how much space to initially allocate is problematic, but since I was using C it wasn't too hard to handle. Surely this is already handled in Smalltalk, C, Ada, etc. Why would Modula-3 be so different? -- --- Darren New --- Grad Student --- CIS --- Univ. of Delaware --- ----- Network Protocols, Graphics, Programming Languages, Formal Description Techniques (esp. Estelle), Coffee, Amigas ----- =+=+=+ Let GROPE be an N-tuple where ... +=+=+= ------------------------------------------------------------------------------ Date: 12 Feb 91 23:33:53 GMT From: ogden@seal.cis.ohio-state.edu (William F Ogden) Subject: Re: checked run-time errors and exceptions (was: running out of memory ) Mick Jordan writes: > ... Although running out of memory is not a programming error, it is >effectively a failure of the Modula-3 abstract machine, and I am curious to >know how you can write portable code to recover from it ... A satisfactory abstract machine can't present the illusion of an infinite memory space when it can't sustain it. All computers are resource bounded, and accidentally running out of memory should be considered a programming error at some level. Invoking the NEW operator with inadequate remaining memory is violating its precondition just as surely as invoking the divide operator with a zero second argument is. The only difference is that while a prudent programmer can always prevent the problem with divide (by checking for a zero if he is unsure of his second argument), there is no general way to know when he is about to misuse NEW. Adding an exception mechanism doesn't solve the problem, because in many situations it is already too late to recover gracefully (or at all). Some additional condition of memory checking operation is needed if the language is going to provide adequate support for sound programming. /Bill ------------------------------------------------------------------------------ Date: Tue, 12 Feb 91 15:49:01 GMT From: Marc Wachowitz Subject: NEW out of memory There were several articles proposing various schemes how to deal with NEW running out of memory. Most of them only considered "polite abortion" of the program. I think it has to be possible to control the effect of every single invocation of NEW; sometimes an application may be able just to ignore the failure of a (in these cases usually large) allocation attempt. Termination handlers are no serious help in this case. For the benefit of existing & innocent programs, one might let the current behavious of NEW unchanged (but extend the language definition to define failure as a checked runtime error, which may be reported in an implemenation-dependent way). A new procedure (say TRYNEW) would be defined to be equivalent to NEW, except that it raises an exception (say NEWFAILURE) on failure. This exception should not be implicitly included in raises clauses, but should for convenience be directly visible (instead of needing to be imported from some interface) in every scope (i.e. be a predefined name). Considering the proposed features to install termination: I do strongly suggest that at least a simple variant be made part of the language spec. It should at least allow each module to register one procedure, which may then take care of data managed by the module (e.g. keeping a list of opened files and closing them on exit; I prefer that over the current scheme of auto-flushing writers). ------------------------------------------------------------------------------ Date: Tue, 12 Feb 1991 13:06:08 PST From: Mike_Spreitzer.PARC@xerox.com Subject: Re: running out of memory Installing global failure-handlers will solve some problems, but how does it solve the following? Imagine a bitmap editor program. It allows the user to open multiple windows on bitmaps from multiple files. At one point, with some windows already open, a user asks to open a new window on the 23 megabyte contents of a particular file. Wouldn't it be nice if the editor could say something like "I'm sorry, I can't now edit a new bitmap that big", rather than "Memory exhausted, saving all files and exiting..." ? If we can only register procedures to do a little work before the editor aborts, I don't see a good way to get the desired behavior. Mike ------------------------------------------------------------------------------ Date: 13 Feb 91 09:00:37 GMT From: schoebel@bs3.informatik.uni-stuttgart.de (Thomas Schoebel) Subject: Re: checked run-time errors and exceptions (was: running out of memory ) In article <1991Feb11.152850.1851@src.dec.com> mjordan@src.dec.com (Mick Jordan ) writes: >something very bizarre about explicitly catching what are truly programming >errors. Although running out of memory is not a programming error, it is >effectively a failure of the Modula-3 abstract machine, and I am curious to >know how you can write portable code to recover from it, given that you must >be very careful not to call any code which might call NEW again. > >Mick Jordan I don't agree with this failure of the abstract machine. There are cases when a programmer wants to use all *available* memory, e.g. when implementing a cache. Such a program will run with less cache memory also. The really amount of memory is runtime dependant, but *not* the semantics of the program. From a general purpose language I will expect to support me with information about available ressorces, allowing to make runtime decicions about ressource managing strategies. Thomas Schoebel E-mail: schoebel@ifi.informatik.uni-stuttgart.de Phone: +49 711 121-1409 ------------------------------------------------------------------------------ Date: 13 Feb 91 08:38:00 GMT From: macrakis@gr.osf.org (Stavros Macrakis) Subject: Re: running out of memory In article <9102120059.AA15796@jumbo.pa.dec.com> gnelson (Greg Nelson) writes: Doug Moen proposes that Modula-3 define an exception to be raised by NEW when storage is exhausted.... Of course, as Dave Emery points out, [the exception handler] has to be coded very carefully.... [Discussion of multithreading problems...] The solution to these problems is to provide an interface through which clients can register procedures to be executed when a program aborts due to a checked runtime error.... There are many cases where running out of memory is not a fatal error which should give rise only to cleanup activities. In particular, in interactive systems, the execution of a given command may exhaust memory, and thus have to be aborted, although the overall interactive environment should recover gracefully. An even simpler case: some network protocols include requests to allocate buffers of given size. If the buffer allocation fails, you should send a NACK to the requester, not die. -s Stavros Macrakis Open Software Foundation Research Institute Mail: 2 av de Vignate, 38610 Gieres (Grenoble), France Net: macrakis@gr.osf.org Phone: +33/76.63.48.82 Fax: +33/76.51.05.32 ------------------------------------------------------------------------------ Date: 13 Feb 91 13:50:47 GMT From: larry@titan.tsd.arlut.utexas.edu (Larry Maturo) Subject: Objects in Modula-3 I have been following comp.lang.modula3 since it's inception. I followed the comp.lang.modula2 group for a little longer. In none of that time have I seen anything discussing the Modula-3 model for objects. I am curious why records were extended to objects instead of having an OBJECT MODULE type of encapsulation. I feel sure this must have been explored and rejected at some point and would like to know what problems were found with this approach. I have downloaded the documentation on Modula-3 and didn't find any discussion there. If there is a document I could download I'd appreciate a reference to it. Thanks. +-----------------------------------+----------------------------------------+ | | | | Larry Maturo | Opinions expressed herein must be | | Applied Research Laboratories | yours, neither I nor my employer have | | University of Texas at Austin | any. | | P.O. Box 8029 +----------------------------------------+ | Austin, Texas 78713-8029 | | | | When you're as great as I am it's hard | | larry @titan.tsd.arlut.utexas.edu | to be modest, but I succeed where | | | others fail. | +-----------------------------------+----------------------------------------+ ------------------------------------------------------------------------------ Date: Wed, 13 Feb 91 11:20:49 EST From: saber.com!wyant@saber.com Subject: Allocation of objects in other than default heap ? Here is a problem that I don't see how to solve elegantly in M3. Suppose I want some objects to be allocated from the process private heap, but some I want allocated out of a heap existing in shared memory (for instance I might be writing a fast local IPC mechanism). I don't see any way in M3 of specifying which heap should be used for a given allocation. Is there in fact a recommended way of doing this ? -- Geoff Wyant wyant@saber.com ------------------------------------------------------------------------------ Date: 13 Feb 91 21:38:46 GMT From: chased@rbbb.Eng.Sun.COM (David Chase) Subject: Re: Allocation of objects in other than default heap ? In article <9102131620.AA13112@riposte> saber.com!wyant@saber.com writes: >... Suppose I want some objects to be allocated from the process >private heap, but some I want allocated out of a heap existing in >shared memory (for instance I might be writing a fast local IPC >mechanism). I don't see any way in M3 of specifying which heap should >be used for a given allocation. Is there in fact a recommended way of >doing this ? I recommend you write an UNSAFE implementation for a SAFE interface (if you can guarantee that it is truly safe). You certainly shouldn't expect to allocate TRACED objects from such a pool (especially for your example), since doing so assumes a certain amount of clairvoyance on the part of the author of the memory allocator and garbage collector. If you decide to allocate UNTRACED OBJECTS from that heap, you'll need to get into bed with the compiler/run-time's representation of TYPECODEs and method suites. This is distinctly non-portable, but so it goes. I realize that this is probably not the helpful answer that you wanted, but programing in unsafe/untraced M-3 is no worse than programming in C, except that it is more verbose. I can see where you might wish for something like C++'s new-at-specified-location; perhaps that should be considered. However, be aware of the following problems with such a scheme: (1) you need an interface that provides the size for the object and for its hidden data structures (2) ynaitp the align for the object etc (3) ynaitp the offset of the user-visible pointer from the actual beginning of the allocated memory for the object. People who are paid to think about these things can no doubt find other problems, and I wouldn't mind hearing about them. For UNSAFE code, it might be handy to be able get this function from a some sort of optional standard interface. Any thoughts from the committee on this? It seems like such an interface could be based on typecodes. (Is this already in 1.6, which I haven't bothered to fetch yet? I'll go look, in my copious free time.) David Chase Sun ------------------------------------------------------------------------------ Date: Wed, 13 Feb 91 16:16:01 PST From: muller@src.dec.com (Eric Muller) Subject: Re: Allocation of objects in other than default heap ? In article <7957@exodus.Eng.Sun.COM>, chased@rbbb.Eng.Sun.COM (David Chase) wri tes: > It seems like such an > interface could be based on typecodes. (Is this already in 1.6, which > I haven't bothered to fetch yet? I'll go look, in my copious free > time.) Pickle has access to an interface in that style (RTHeapRep). While reading from a file, it will rebuild pages that are acceptable to RTHeap and declare these available when it is done. -- Eric. ------------------------------------------------------------------------------ Date: Thu, 14 Feb 1991 13:12:07 GMT From: jch@dyfed.rdg.dec.com (John Haxby) Subject: Re: NEW out of memory Checking for lack of memory doesn't really help in two out of three cases that I can think of. The good case is when the program has simply run up against th e heap limit (eg 32Mb on ULTRIX 4.1) in which case, the program should abort gr acefully--there are problems with this that I'll get to in a minute. The bad ca ses are two variants of the system running out of resources for (virtual) memor y. In simple VM schema (like ULTRIX's) where space is pre-allocated or pre-rese ved, running out is fairly serious as programs start failing all over the place; to be honest, in this scenario, sys tem managers are likely to treat large processes (eg editors written in modula 3 consuming lots of memory) as fair game and any attempt by the program to stru ggle on is going to be fruitless. Admitedly, the user could choose to terminate gracefully, apart from the problem that I'll get to in a minute. The second va riant of running out of VM resources is found in systems that do lazy allocatio n of resources (eg Mach) where the ru nning program is happily allocating memory, but when it tries to use that memor y it gets peremptorily killed. The problem that I have been getting on to in a minute is what happens when the running process gets an exception to say that no more memory is available. Wha t can it do? The answer is anything that doesn't involve allocating memory. In practice, I suspect, this wouldn't even including the editor writing files out to disk and freeing up associated buffers (at least not without some very caref ul programming). In any language where storage allocation is cheap and easy, th ere is a lot of it (although I don't know exactly how this affects Modula 3), and when you run out you may as well g ive up. The question arises: how much care *should* you take to deal with lack of VM re source? Well, how often do you run out? The attitude that Mach takes is "almos t never, so you can allow deliberate over-allocation". Experience bears this o ut: I have been using an editor (admitedly not written in Modula anything) for several years that simply dies on lack of memory, and it has never died on me u nless the system has gone down as well. So why go to all that trouble? It is *possible* to run out of VM, but it d oesn't happen all that often, and when it does, the consequences are so serious for everything that the odd application keeling over makes little difference. Reading that last paragraph again, the begging question is "under what circumst ances *must* you deal with lack of memory?" The answer seems to be "when the ap plication is critical". Under these circumstances, the application must take st eps to ensure that it doesn't run short of memory when it needs it. The obvious way to do this is to ask the operating system how much memory can be allocated to this process before before it runs out of space. This, obviously, is highly O/S dependent, and some VM systems ( eg Mach's) may not even know how much space can be allocated before resources b ecome a problem. The way *not* to do this is to introduce a "TRYNEW" request--t his is too late. You need to know if VM shortfall is going to be a problem soo n, not right now. If you need to allocate space to deal with the kidney dialysi s (or however you spell it) in order to keep the patient alive, then "I'm sorr y, I can't allocate enough memory is going to help" You need to know well in ad vance that in order to allocate enoug h memory for what you might need to do that you need to kill that emacs or that motif application. This problem gets to the stage where we no longer deal with language constructs, but operating system services and application requirement s to *avoid* the lack of memory exceptions, not to do something sensible when y ou get them, because nothing sensible can be done at that late stage. [Sorry if all this is garbled, it's written in a bit of a rush and I haven't ha d time to review it properly (to all those who say that I shouldn't have writte n it in the first place, I am inclined to agree] -- John Haxby, Definitively Wrong. Digital Reading, England <...!ukc!wessex!jch> ------------------------------------------------------------------------------ Date: Thu, 14 Feb 1991 07:40:19 PST From: Mike_Spreitzer.PARC@xerox.com Subject: Re: NEW out of memory Indeed, it seems that the way for a careful program to avoid allocating itself into a corner involves a two-level allocation scheme. At the lower level, where NEW works, there must be multiple heaps; let's call these ZONEs. Each invocation of NEW is given the ZONE from which the object is to be allocated. At the upper level, ZONEs are created with specific sizes, and associated with specific user requests. The necessary size of a ZONE is computed by the program from the user request. If the ZONE creation fails, the user is told his request can't be satisfied. If the ZONE creation succeeds, the request is granted and worked on. The idea is that the programmer has estimated, calculated, or measured a relation between user requests and the amount of memory needed to satisfy those requests. This brings the failure point forward to the time at which refusal of service is reasonable. In the dialysis example, the user request would be something like "maintain a new patient of such-and-such a weight, blood type, etc". If there isn't enough memory to do that, the user finds out about it up front, and has a chance to take plausible corrective action (try a different ward, kick off some other users, whatever). I know estimating and measuring are not really good enough in critical applications. And calculating can be very tedious or outright impossible, depending on the structure of the application. But what choice is there, really? I take comfort in the philosophy that abstractions are for programmers to make and compilers (and other tools) to break, and thus the hope that I won't have to do such calculations by hand. Mike ------------------------------------------------------------------------------ Date: 14 Feb 91 17:09:14 GMT From: new@ee.udel.edu (Darren New) Subject: Re: NEW out of memory Here's some more ideas on the subject: The idea that a non-portable interface is needed to have a failure-proof NEW seems to me as silly as a non-portable end-of-file indication and as dangerous as an "open" call that crashes when the file doesn't exist. I feel that every run-time error should be able to be caught/avoided by a program; how else does one make "robust" programs? The argument that "If there just isn't any more memory then there just isn't anything you can do" seems invalid given that plenty of C, Smalltalk, Ada, etc programs seem to handle the problem just fine. I therefore throw out the following ideas for discussion. The main idea is that there is a "safe" allocator called TRYNEW and a "dangerous" allocator called NEW. TRYNEW will fail before NEW does, allowing applications written with TRYNEW to stop allocating memory while still leaving enough for routines to use NEW. WHEREAS changing the semantics of NEW is unacceptable, except possibly to require that running out of memory raise an exception, WHEREAS running out of stack space is probably going to be difficult to catch, even with an exception handler, WHEREAS different threads can request allocations concurrently, making check-then-allocate designs awkward, WHEREAS different implementation modules may need different amounts of memory even for the same interface module, WHEREAS the OS may not provide any way of finding out how much memory is left except via failed allocations, WHEREAS NEW() has a syntax impossible for the user to duplicate in his/her own interface module, WHEREAS running out of memory should not cause one to be unable to allocate more memory WITNESSETH the following proposed interface, in which "Traced" can be replaced by "Untraced" and duplicated in order to handle both heaps. TYPE ReserveStatusType = {Unreserved, Reserved, Exhausted}; EXCEPTION NOMEM(size : INTEGER); Size is the number of kilobytes in the request that caused the failure. PROCEDURE SetTracedRequirements(minimum, maximum : INTEGER := 0) RAISES {NOMEM} ; This sets the minimum and maximum reservation amounts atomically. This will reserve memory for NEW, not TRYNEW. A -1 means no change. Here and everywhere, measurements are in KBytes. Clears an "Exhausted" error if one exists and enough memory is now available. PROCEDURE IncTracedRequirements(minimum, maximum : INTEGER := 0) RAISES {NOMEM} ; This increments the minimum and maximum reservation amounts by the indicated amounts atomically. Typically, this would be used to reserve memory by calling it during the body of a library module. Can be called with (0,0) to determine if enough memory is available. Clears an "Exhausted" error if one exists and enough memory is now available. PROCEDURE DecTracedRequirements(minimum, maximum : INTEGER := 0) RAISES {NOMEM} ; This decrements the minimum and maximum reservation amounts by the indicated amounts atomically, handy when a module stops needing memory. Clears an "Exhausted" error if one exists and enough memory is now available. PROCEDURE GetTracedRequirements(VAR maximum, maximum : INTEGER); This returns the current minimum and maximum reservation amounts atomically. PROCEDURE GetTracedReserveStatus() : ReserveType; This returns one of three statuses: Unreserved means that either the maximum reserve is zero or that TRYNEW has not been called or there is enough memory left and the runtime system knows that. Reserved means that some memory has actually been allocated. Exhausted means that an allocation has failed since last time the requirements were set. PROCEDURE RegisterTraced(PROCEDURE P(size : INTEGER) RAISES {NOMEM}, priority : INTEGER); P will be one of the procedures called in an attempt to free up memory. It will be called after all procedures of a lower priority have been called and failed. PROCEDURE DeregisterTraced(PROCEDURE P(size : INTEGER) RAISES {NOMEM}); P will no longer be called in an attempt to free up memory. Then "TRYNEW" (whose name I don't like but I can't think of anything better, maybe MALLOC?) would behave as follows: Initially, no memory is reserved and GetTracedReserveStatus returns Unreserved. No procedures are registered. TRYNEW will always succeed if (maximum <= available). TRYNEW will always fail if (available < minimum) or (ReservedStatus = Exhausted). If TRYNEW fails, it first calls each registered procedure repeatedly with the amount of memory it is trying to allocate. If the procedure does not raise an exception, it retries. If the procedure does raise an exception, it tries the next procedure. When all procedures have raised exceptions, it sets the reserved status to Exhausted and raises NOMEM. Maybe we would want a TRYNEW that does not call the procedures, or only calls some (up to a certain priority), allowing (for example) caches of disk information to not be flushed by attempts to allocate caches for in-RAM information. But this seems to be getting excessive. Hence, libraries that cache could use RegisterTraced to free buffers and such. Libraries that use NEW (especially during cleanup) could use IncTracedRequirements at the start of the module body. Libraries that don't really care could simply continue to use NEW. Attempts to allocate the last little bit of memory via NEW would work but via TRYNEW would fail. If the runtime system cannot determine how much memory is available easily or quickly, this could be implemented by actually allocating the maximum amount of memory and then when NEW fails, deallocating that memory and trying again. -- Darren -- --- Darren New --- Grad Student --- CIS --- Univ. of Delaware --- ----- Network Protocols, Graphics, Programming Languages, Formal Description Techniques (esp. Estelle), Coffee, Amigas ----- =+=+=+ Let GROPE be an N-tuple where ... +=+=+= ------------------------------------------------------------------------------ Date: Thu, 14 Feb 91 13:02:25 -0500 From: Norman Ramsey Subject: Formatter doesn't flush trailing blanks If the last character of a text is a blank, it doesn't get flushed correctly by Formatter.Flush. Evidence is below. This problem isn't affected by Jorge's fix. I assume that this problem is related to the special treatment of blanks in Formatter.PutChar (they don't get added to t.chars, but instead a special Ref containing only the blank is added). I can't quite puzzle out what's wrongby looking at Formatter.Flush and Formatter.WaitUntilEmpty. Norman nr@hart (22) % m3 Bugs15.mo Formatter.?o nr@hart (23) % a.out Formatter.T: input `prompt> ' emerges as `prompt>'. Formatter.T: input `prompt> ' emerges as ` prompt>'. Formatter.T: input `hello there' emerges as ` hello there'. Formatter.T: input `army boots! ' emerges as `army boots!'. Wr.T: input `prompt> ' emerges as `prompt> '. Wr.T: input `prompt> ' emerges as `prompt> '. Wr.T: input `hello there' emerges as `hello there'. Wr.T: input `army boots! ' emerges as `army boots! '. # To unbundle, "sed '1,/^# To unbundle/d' < thisfile | sh" # Thu Feb 14 12:58:23 EST 1991 echo Bugs15.m3 1>&2 sed 's/^-//' >'Bugs15.m3' <<'End of Bugs15.m3' -MODULE Bugs15 EXPORTS Main; -IMPORT Fmt, Formatter, Stdio, TextWr, Wr; - -VAR - under := TextWr.New(); - wr := Formatter.New(under); - -CONST - test = "prompt> "; - -PROCEDURE Check(msg:TEXT) = -BEGIN - Formatter.PutText(wr,msg); - Formatter.Flush(wr); - Wr.Flush(Formatter.UnderlyingWr(wr)); - - Wr.PutText(Stdio.stdout, - Fmt.F("Formatter.T: input `%s\' emerges as `%s\'.\n",msg,TextWr.T o Text(under))); -END Check; - -PROCEDURE Check2(msg:TEXT) = -BEGIN - Wr.PutText(under,msg); - Wr.Flush(under); - - Wr.PutText(Stdio.stdout, - Fmt.F("Wr.T: input `%s\' emerges as `%s\'.\n",msg,TextWr.ToText(u n der))); -END Check2; - -BEGIN - Check(test); - Check(test); - Check("hello there"); - Check("army boots! "); - Check2(test); - Check2(test); - Check2("hello there"); - Check2("army boots! "); -END Bugs15. - End of Bugs15.m3 ------------------------------------------------------------------------------ Date: 14 Feb 91 18:55:44 GMT From: jonb@vector.Dallas.TX.US (Jon Buller) Subject: Re: NEW out of memory This situation has been known to occur often on 128K Macs (sometimes on larger ones too 8-). What is usually done there, is to pre-allocate a block of memory (10KB seems good for many apps, adjust to taste). When New fails, deallocate the block, clean up, and shut down (or, let the user close some files, windows, and what-have-you, and keep running). If this is done, an exception from New is about the best thing I would want to happen, since I can go into my rainy-day store of memory when I get the exception, and quit. Or, if I'm careful, clean up memory, re-allocate my emergency memory block, and keep going. In any case, I like getting exceptions better than simply getting NIL, or getting my program killed and losing everything. I can handle getting NIL back, but I would probably just use My_New which calls New, and raises an exception if it failed. I once had to write code that checked each allocation, and jumped out of 5 or so procedure calls to return to a main loop on failure, and try to recover. It was in a Pascal without inter-block goto's, so I had to resort to large if-then's in every procedure. The result was MUCH more difficult to read, debug, and look at. I wish I had this kind of stuff back then... -- Jon Buller jonb@vector.dallas.tx.us ..!texsun!vector!jonb FROM Fortune IMPORT Quote; FROM Lawyers IMPORT Disclaimer; ------------------------------------------------------------------------------ Date: 14 Feb 91 21:02:31 GMT From: nr@hart.Princeton.EDU (Norman Ramsey) Subject: Specializing objects Suppose I have two object types TYPE T = OBJECT mumble, fritz: INTEGER; END; TA = T OBJECT a: BOOLEAN; END; and suppose further that I am handed a T and want to produce a TA with the property that every field of the TA has the same value it had in the T. I can't think of anything any better than: PROCEDURE Narrow(o:T):TA = BEGIN RETURN NEW(TA, mumble := o.mumble, fritz := o.fritz, a := ); END Narrow; Is there some way for me to do this without having to enumerate all the fields of T? If not, I am bound to break my programs by extending T and forgetting to update Narrow. -- Norman Ramsey nr@princeton.edu ------------------------------------------------------------------------------ Date: 14 Feb 91 23:06:13 GMT From: nr@elan.Princeton.EDU (Norman Ramsey) Subject: signal handling on the DS3100 for the SRC implementation The interface Usignal doesn't work correctly on the DS3100 (MIPS R2000 architecture) running Ultrix 4.0. Here's an interface that does work. INTERFACE MipsSignal; IMPORT Formatter; FROM Ctypes IMPORT int; CONST BRK_USERBP = 0; TYPE struct_sigcontext = RECORD sc_onstack : int; (* sigstack state to restore *) sc_mask : int; (* signal mask to restore *) sc_pc : int; (* pc at time of signal *) (* * General purpose registers *) sc_regs : ARRAY [0..31] OF int; (* processor regs 0 to 31 *) sc_mdlo : int; (* mul/div low *) sc_mdhi : int; (* mul/div high *) (* * Floating point coprocessor state *) sc_ownedfp : int; (* fp has been used *) sc_fpregs : ARRAY [0..31] OF int; (* fp regs 0 to 31 *) sc_fpc_csr : int; (* floating point control and status reg *) sc_fpc_eir : int; (* floating point exception instruction reg *) (* * END OF REGION THAT MUST AGREE WITH setjmp.h * END OF jmp_buf REGION *) (* * System coprocessor registers at time of signal *) sc_cause : int; (* cp0 cause register *) sc_badvaddr : int; (* cp0 bad virtual address *) sc_badpaddr : int; (* cpu bd bad physical address *) END; struct_sigvec = RECORD sv_handler : SignalHandler; sv_mask : int; sv_flags : int; END; SignalHandler = PROCEDURE (sig, code: int; scp: UNTRACED REF struct_sigcontext); <*EXTERNAL*> PROCEDURE sigvec (sig: int; VAR vec, ovec: struct_sigvec): int; TYPE jmp_buf = RECORD jb_onstack : int; (* sigstack state to restore *) jb_mask : int; (* signal mask to restore *) jb_pc : int; (* pc at time of signal *) (* * General purpose registers *) jb_regs : ARRAY [0..31] OF int; (* processor regs 0 to 31 *) jb_mdlo : int; (* mul/div low *) jb_mdhi : int; (* mul/div high *) (* * Floating point coprocessor state *) jb_ownedfp : int; (* fp has been used *) jb_fpregs : ARRAY [0..31] OF int; (* fp regs 0 to 31 *) jb_fpc_csr : int; (* floating point control and status reg *) jb_fpc_eir : int; (* floating point exception instruction reg *) jb_padding : ARRAY [0..10] OF int; END; <*EXTERNAL "_setjmp" *> PROCEDURE WildSetjmp (VAR env: jmp_buf): int; <*EXTERNAL "_longjmp"*> PROCEDURE WildLongjmp (VAR env: jmp_buf; val: int); <*EXTERNAL "setjmp"*> PROCEDURE Setjmp (VAR env: jmp_buf): int; <*EXTERNAL "longjmp"*> PROCEDURE Longjmp (VAR env: jmp_buf; val: int); CONST SigDescriptions = ARRAY OF TEXT { "signal 0", "hangup", "interrupt", "quit", "illegal instruction", "trap", "IOT instruction", "EMT instruction", "floating point exception", "kill", "bus error", "segmentation violation", "bad argument to system call", "write on a pipe with no one to read it", "alarm clock", "software termination signal from kill", "urgent condition on IO channel", "sendable stop signal not from tty", "stop signal from tty", "continue a stopped process", "to parent on child stop or exit", "to readers pgrp upon background tty read", "like TTIN for output if (tp->t_local<OSTOP)", "input/output possible signal", "exceeded CPU time limit", "exceeded file size limit", "virtual time alarm", "profiling time alarm", "window size changes", "Sys-V rec lock: notify user upon server crash", "User signal 1 (from SysV)", "User signal 2 (from SysV)" }; FpeDescriptions = ARRAY OF TEXT { "fpe 0", "integer overflow", "integer divide by zero", "floating overflow", "floating/decimal divide by zero", "floating underflow", "decimal overflow", "subscript out of range", "divide by zero floating fault", "floating underflow fault" }; TYPE BRK = { USERBP, KERNELBP, ABORT, BD_TAKEN, BD_NOTTAKEN, SSTEPBP, OVERFLOW, DIVZERO, RANGE, STACKOVERFLOW }; CONST BRKDescriptions = ARRAY BRK OF TEXT { "user bp (used by debuggers)", "kernel bp (used by prom)", "no longer used", "for taken bd emulation", "for not taken bd emulation", "user bp (used by debuggers)", "overflow check", "divide by zero check", "range error check", "used by Ada" }; END MipsSignal. -- Norman Ramsey nr@princeton.edu ------------------------------------------------------------------------------ Date: Thu, 14 Feb 91 23:00:44 PST From: muller@src.dec.com (Eric Muller) Subject: Re: Typefinger printing in SRC M3 In article <9102111538.AA24105@yough.ucc.umass.edu>, hudson@yough.ucc.umass.edu (Rick Hudson) writes: > My surface impression from scanning the sources is that works as > follows. Each type (scalar or constructed) has an operation to return > a textual type name. When fingerprinting a constructed type (esp. an > object/record) a string is constructed containing the recursive > enumeration of all scalar type names used in that constructed type. > This string is then treated as input to the "poly" function to compute > a 64-bit fingerprint. > Is this reasonably accurate or am I way out in left field >> This is accurate. In addition, you have to consider the case of opaque types. The final phase of linking (practically, when the program starts, but conceptually at link time) knows everything about the types and computes a "full" fingerprint, based on the fingerprints of the partial revelations. For example, INTERFACE I; TYPE T <: ROOT; END I. MODULE M; TYPE U = T OBJECT a: INTEGER; END; END M. When compiling M, we generate a partial fingerprint for "OBJECT a: INTEGER". A link time, this partial fingerprint is combined with the fingerprint of type to the right of the "REVEAL T =" that must be somewhere in the program. As for the probability of collisions, it is extremely low. You can ask Andrei Broder (broder@src.dec.com) if you want to know the fine points. -- Eric. ------------------------------------------------------------------------------ Date: Thu, 14 Feb 91 23:14:34 PST From: muller@src.dec.com (Eric Muller) Subject: Re: Specializing objects In article <6929@rossignol.Princeton.EDU>, nr@hart.Princeton.EDU (Norman Ramsey ) asks: > > Suppose I have two object types > > TYPE > T = OBJECT > mumble, fritz: INTEGER; > END; > > TA = T OBJECT > a: BOOLEAN; > END; > > and suppose further that I am handed a T and want to produce a TA with > the property that every field of the TA has the same value it had in > the T. I can't think of anything any better than: > > PROCEDURE Narrow(o:T):TA = > BEGIN > RETURN NEW(TA, mumble := o.mumble, fritz := o.fritz, a := ); > END Narrow; > > Is there some way for me to do this without having to enumerate all > the fields of T? If not, I am bound to break my programs by extending > T and forgetting to update Narrow. Given that RTHeap has a Duplicate procedure: PROCEDURE Duplicate (r: REFANY): REFANY; (* return a reference to a new allocated copy of the referent, not recursively applied to internal references. Duplicate(r) is equivalent to Allocate (TYPECODE (r)). *) I could provide: PROCEDURE ?? (r1, r2: ROOT): ROOT; (* The type of r1 must be a subtype of the type of r2. The fields of r2 are (non-recursively) copied to the corresponding fields of r1 *) You could write: oa := ?? (NEW (TA, a = ), o); Opinions ? eric. ------------------------------------------------------------------------------ Date: Thu, 14 Feb 91 23:21:04 PST From: muller@src.dec.com (Eric Muller) Subject: Re: signal handling on the DS3100 for the SRC implementation In article <6935@rossignol.Princeton.EDU>, nr@elan.Princeton.EDU (Norman Ramsey ) writes: > > The interface Usignal doesn't work correctly on the DS3100 (MIPS R2000 > architecture) running Ultrix 4.0. Here's an interface that does work. I knew this would happen sooner or later. Let's face it, there is no such thing as Ultrix 4.0; there is Ultrix 4.0/VAX and there is Ultrix 4.0/MIPS. I guess the proper solution is be to have two binding libraries. I'll work on that. Any suggestions for the names ? -- Eric. ------------------------------------------------------------------------------ Date: Wed, 13 Feb 91 12:13:35 PST From: frode@toaster.SFSU.EDU (Frode Odegard) Subject: Objects in Modula-3 Module types (with support for multiple inheritance, even) are provided in Modula-90, an experimental ETH extension of Modula-2. Two papers were presented (by Beat Heeb and Martin Odersky) at the "1st International Modula-2 Conference" in Yugoslavia, October 1989. Odersky is now doing research for IBM on functional languages but I think Heeb is there still. The undersigned is working on another paper on module types, exploring the posibility of introducing them in when the ISO standard for Modula-2 is revised. - Frode ------------------------------------------------------------------------------ Date: Fri, 15 Feb 91 02:46:15 PST From: Subject: Re: Formatter doesn't flush trailing blanks > [Norman Ramsey:] If the last character of a text is a blank, it > doesn't get flushed correctly by Formatter.Flush. This is a feature, not a bug. The Formatter code takes pains to suppress trailing blanks before line breaks (check DoPrintChar). You probably can solve your problem by using PutText(" ", raw := TRUE), instead of PutChar or plain PutText. The "raw" flag causes it to treat blanks as ordinary printing characters. By the way, note that Formatter.Flush is not a harmless operation like Wr.Flush, that you can insert anywhere without affecting the output. Formatter.Flush also closes all pending Begin and Align groups, and chooses breakpoints as if the expression ended there. (I intend to add a harmless Flush once Eric gets the ESP package working. 8-) --jorge ------------------------------------------------------------------------------ Date: 16 Feb 91 10:26:30 GMT From: sfk@otter.hpl.hp.com (Steve Knight) Subject: Request: Err ... what's modula-3? I really hate to post this here ... but I've failed to find anyone who can give me a overview of the differences between modula-2 and modula-3. So I'd be grateful for an outline or references on this topic. (Helpful replies will be posted back.) TIA, Steve Knight sfk@hplb.hpl.hp.com ------------------------------------------------------------------------------ Date: 17 Feb 91 18:17:47 GMT From: mfranz@bernina.ethz.ch (Michael Franz) Subject: MacOberon 2.0 Available MacOberon is the Macintosh implementation of the Oberon System, an innovative computing environment created by Niklaus Wirth and Jurg Gutknecht at the Swiss Federal Institute of Technology in Zurich. Niklaus Wirth is the creator of the Pascal and Modula-2 programming languages. The Oberon System is based on the Oberon programming language, the successor to Modula-2. The Oberon system is user-extensible to an extent that is unparalleled in traditional systems. The whole system may be viewed as an extensible Hypertext editor, to which a user may add commands (written in Oberon, a language very similar to Pascal) at any time. Version 2.0 of MacOberon is now available. This includes, among other things, complete documentation, a document editor, a graphics editor, an intelligent text editor, an Oberon compiler and many source programs. MacOberon is completely free. It runs on Macintosh II computers (or any other Macintosh that has a 68020 or 68030 processor). You will need about 1.5 Megabytes of free disk space for installing the system. MacOberon may be obtained via anonymous FTP from 129.132.101.33 - user: anonymous - password: your e-mail-address Michael Franz, Computersysteme, ETH Zurich, Switzerland -- Michael Franz franz@inf.ethz.ch +41-1-254'73'14 Computersysteme ETH Zentrum 8092 Zurich Switzerland -- Michael Franz franz@inf.ethz.ch +41-1-254'73'14 Computersysteme ETH Zentrum 8092 Zurich Switzerland ------------------------------------------------------------------------------ Date: 18 Feb 91 19:56:07 GMT From: nr@elan.Princeton.EDU (Norman Ramsey) Subject: Closures in m3 Many of the table, list, et cetera data structure packages make it possible to enumerate elements of the table. Typically the procedures implementing the enumeration accept a REFANY as arguments, and this REFANY is used as a closure argument. An example from List: TYPE WalkProc = PROCEDURE (arg, element: REFANY) RAISES {}; (* The type of procedures for walking over lists. *) PROCEDURE Walk( l: T; p: WalkProc; arg: REFANY := NIL ); (* Applies p to each of the elements of l in turn. The only exceptions raised are those possibly raised by p. *) I suggest that it would be more elegant for enumeration to use closures (as in Thread). TYPE WalkClosure = OBJECT METHODS apply(element:REFANY) END; PROCEDURE Walk( l: T; p: WalkClosure ); I would like to hear people's comments on this proposal. If you won't buy ``more elegant,'' perhaps you'll buy ``typechecked at compile time.'' -- Norman Ramsey nr@princeton.edu ------------------------------------------------------------------------------ Date: Mon, 18 Feb 91 13:52:57 PST From: Subject: Re: Closures in m3 I would like to second Norman Ramsey's proposal. Stamp out REFANYs! ------------------------------------------------------------------------------ Date: 19 Feb 91 03:44:22 GMT From: nr@hart.Princeton.EDU (Norman Ramsey) Subject: Pkl question The documentation for Pkl.i3 says ``procedures are preserved.'' Is this true for different programs containing some of the same modules, or true only for different invocations of the same program? If true for different programs, how is it done? Since the documentation says methods are restored to the default, I assume methods are so restored even for different programs. Correct? -- Norman Ramsey nr@princeton.edu ------------------------------------------------------------------------------ Date: Mon, 18 Feb 91 22:31:31 PST From: Subject: Re: Pkl question The documentation for Pkl.i3 says ``procedures are preserved.'' Is this true for different programs containing some of the same modules, or true only for different invocations of the same program? If true for different programs, how is it done? When Pkl.Write encounters a procedure, it writes the procedure's qualified name and signature into the pickle. The program reading the pickle must contain a procedure with the same qualified name and signature, or else Pkl.Read will raise an exception. Different programs reading the pickle could have different compiled code for the procedure. Methods are treated the same way. ------------------------------------------------------------------------------ Date: Mon, 18 Feb 91 19:39:35 EST From: David Halko Subject: Modula3 I am very interested in Modula3. Modula2 is available under the Microware OS9-68K platform from various vendors in several different countries now, but object code produced by the system has a tendency of being large (compared to the extremely efficient C Compiler, which crunches code down to rediculously shrunken levels.) A language which keeps most of the similarities and basic features of Modula2 would be a plus, and even more importantly, and advantage, if translated down to C Code so our system could compile it the rest of the way down. Although not currently available under the OS9 68K real-time environment, I would expect someone out there to complete a version rather soon since the environment is so close to UNIX. Using Sparc Stations, I also have a need to determine what features it presents, now. If there is an electronic mailing list concerning the Modula3 system in development, I would be most grateful if you would include me on that list. My electronic mail address is halkoD@moravian.edu Thankyou, David J. Halko ------------------------------------------------------------------------------ Date: 19 Feb 91 08:32:41 GMT From: h5346866@rick.cs.ubc.ca (bradley don head) Subject: Modula3 port to NeXT? Does anyone know of a port of Modula3 to the NeXT? If not, can I get the source somewhere? Thanks in Advance --------------------------------------------------- Brad Head University of British Columbia h5346866@rick.cs.ubc.ca ------------------------------------------------------------------------------ Date: Tue, 19 Feb 91 10:55:00 PST From: muller@src.dec.com (Eric Muller) Subject: Re: Modula3 port to NeXT? In article <1991Feb19.083241.4394@rick.cs.ubc.ca>, h5346866@rick.cs.ubc.ca (bra dley don head) writes: > Does anyone know of a port of Modula3 to the NeXT? If not, can I get the > source somewhere? I am not aware of a port of SRC Modula-3 to the NeXT machine, but it should not be too difficult. You can get the sources via anonymous ftp from gatekeeper.dec.com, in pub/DEC/Modula3. You will also find there the report, and an archive of the messages sent to comp.lang.modula3 (and its precessor, the m3@src.dec.com mailing list). We do not have the ressources to make tapes, and the system is too big to be sent by mail. Eric. ------------------------------------------------------------------------------ Date: 19 Feb 91 20:10:41 GMT From: new@ee.udel.edu (Darren New) Subject: Re: NEW out of memory I tried to mail this, but it bounced. => EXCEPTION NOMEM(size : INTEGER); => Size is the number of kilobytes in the request that caused => the failure. => => Huh? Why kilobytes? Suppose I run out of memory requesting 10 bytes. => That's closer to 0K than 1K--it's conceptually unclean to run out of => memory when requesting none of it. Is there some sort of good reason => for this unnecessary factor of 1000? Well, I was thinking that integers may not be big enough to hold an address. That is, you could have more addressable units than integers, making large allocations difficult to describe. (I believe I saw something like this in libraries somewhere, where the base-2 log of a size was used instead of the actual size.) Naturally, the request would be rounded up to the next 1024 unit. The intention of giving the size was to allow the program freeing memory to only free what was needed by somebody else. For example, a disk cache could only flush and free 20 buffers instead of all 600. Freeing too much would not be a problem. (Nor would freeing too little in my scheme, due to the retries.) -- Darren -- --- Darren New --- Grad Student --- CIS --- Univ. of Delaware --- ----- Network Protocols, Graphics, Programming Languages, Formal Description Techniques (esp. Estelle), Coffee, Amigas ----- =+=+=+ Let GROPE be an N-tuple where ... +=+=+= ------------------------------------------------------------------------------ Date: Tue, 19 Feb 91 13:12:49 PST From: mjordan@src.dec.com (Mick Jordan) Subject: Re: Closures in m3 > I would like to hear people's comments on this proposal. If you won't > buy ``more elegant,'' perhaps you'll buy ``typechecked at compile time.'' 'List' is an example of an interface that was simply copied from the M2+ world, hence the REFANY. It is certainly not the best way to do callback iterators in Modula-3, which are neatly handled by 'object closures' as you suggest, particularly given that state needed by the WalkProc can be bundled up as a subtype of WalkClosure. Actually, for simple data structures I prefer the following style of iterator: VAR someContainerType: SomeContainerType.T; containedType: ContainedType.T; iter := SomeContainerType.NewIter(someContainerType); BEGIN WHILE SomeType.Next(iter, containedType) DO (* process 'containedType' *) END; END; This has the virtue that you do not need to bundle up state into the object closure, nor do you need to code a new procedure. Finally, note that, because Modula-3 permits nested procedures to be passed as arguments, the closure REFANY ('arg') isnt really necessary since local state can be accessed directly from the nested procedure. However, not everybody likes nested procedures. Mick Jordan ------------------------------------------------------------------------------ Date: 20 Feb 91 21:32:11 GMT From: beser@tron.UUCP (Eric Beser) Subject: oberon on sun in one font? I downloaded oberon for the sparc station and I noticed that the windows were crowded over to the right of the screen. There was only one font. Is there some configuration other than setting the environment variable so that other fonts may be displayed. Having everything crowded against the right (although readible) seems to me a poor way of displaying the user interface. It seems like I am missing something. There are a lot of fonts... They all sit in the /home/local/oberon directory with the *.Obj files. What am I doing wrong here? Another question... Is there a way of "sealing off" an application so to hide the details of the development environment? I would like to build a software library mechanism to do hypertext searching of software components. I would like to use some of the essense of the GUI, but not all. How does one do this? Thanks, Eric Beser Westinghouse Aerospace Software Engineering ------------------------------------------------------------------------------ Date: Tue, 19 Feb 1991 08:09:28 PST From: Mike_Spreitzer.PARC@xerox.com Subject: Re: Closures in m3 An even simpler approach would be to use procedures without the REFANY argument, relying on the ability to make local procedures as the mechanism for binding to other data. For example, List could be changed to say: TYPE WalkProc = PROCEDURE (element: REFANY) RAISES {}; (* The type of procedures for walking over lists. *) PROCEDURE Walk( l: T; p: WalkProc ); (* Applies p to each of the elements of l in turn. The only exceptions raised are those possibly raised by p. *) A client that sums the element of a list might then look like this: PROCEDURE SumIt( it: List.T ) : INTEGER = VAR sum: INTEGER; PROCEDURE AddElt ( element: REFANY ) = BEGIN (* code to add the element goes here *) END AddElt; BEGIN sum := 0; List.Walk(it, AddElt); RETURN sum; END SumIt; Since the calls on enumerations are from procedure bodies already, there is always a plausible place to put the local procedure declaration. Designing enumeration interfaces raises the issue of concurrency. The above clearly works if the enumeration is sequential and List.Walk does not return untill all the "callbacks" have been made. If enumeration is not sequential, the interface ought to say so (in M3, this can only be done with English comments), and the client has to prepare for it by doing the necessary synchronization, if any, in the local procedure. If List.Walk might return before all the callbacks have been done, List.Walk will have to also return a cookie that the client can use to synchronize with the completion of the enumeration. All three schemes (extra REFANY parameter, closure objects, local procedures) could be made to work if such considerations are made [but the local procedure scheme needs an additional change: the ability to assign local procedures to procedure variables; this is currently forbidden, but no more unsafe than dereferencing UNTRACED references --- so I don't see why it shouldn't be equally possible.] Proceeding further along the local procedure direction, we get the elegance of CLU iterators. Imagine that M3 has a syntax for procedure literals. For example, suppose that LAMBDA sig = Block is the syntax for procedure literals. Then the SumIt could look like this: PROCEDURE SumIt( it: List.T ) : INTEGER = VAR sum: INTEGER; BEGIN sum := 0; List.Walk(it, LAMBDA (element: REFANY) = BEGIN (* add element *) END); RETURN sum; END SumIt; Thus, programmers can create their own kinds of looping constructs, and use only requires a little syntactic boilerplate. That boilerplate could be further minimized if the syntax Block were allowed for procedure literals when the context of the literal provides the type (ie, signature) for the procedure. With that proviso, SumIt could look like this: PROCEDURE SumIt( it: List.T ) : INTEGER = VAR sum: INTEGER; BEGIN sum := 0; List.Walk(it, BEGIN (* code to add the element goes here *) END); RETURN sum; END SumIt; The latter gets too syntactically fragile for my taste when the Block has declarations. But then, declarations before the BEGIN rather than after tastes inferior to me. Adding a new kind of bracket to make blocks into procedure literals is possible, but that's more complex than I'd like to see. Mike ------------------------------------------------------------------------------ Date: 20 Feb 91 23:57:43 GMT From: nr@atomic.Princeton.EDU (Norman Ramsey) Subject: Pkl question Suppose I have a procedure for saving a single copy of any TEXT: PROCEDURE Unique(s:TEXT):TEXT; such that for all s, s1, s2 Text.Equal(s,Unique(s)); and IF Text.Equal(s1,s2) THEN Unique(s1) = Unique(s2) Is there a way for me to use this procedure with the Pkl interface to guarantee that pickles I write won't contain more than one copy of any TEXT? -- Norman Ramsey nr@princeton.edu ------------------------------------------------------------------------------ Date: 21 Feb 91 13:18:44 GMT From: siebren@rivm.nl (Siebren van der Zee) Subject: Multiple inheritance Just curious: how would multiple inheritance fit in Modula 3's type system? I.e. suppose one could write: TYPE t1 = OBJECT METHODS read(buf: Text.T); write(buf: Text.T) END; TYPE t2 = OBJECT hip, hurray: (iene, miene, mutte); METHODS foo(); bar() END; TYPE t3 = t1, t2 OBJECT METHODS yetAnotherMethod() END; VAR foo: t3; How would a procedure that implements yetAnotherMethod be declared? What C-code could the compiler produce to compute the address of foo.hip? Which '<:' relations exist between t1, t2 and t3? (There are probably syntax errors in the above code. I got just about everything to use M3: enthusiasm, a computer. I only lack the compiler...) Siebren ------------------------------------------------------------------------------ Date: 21 Feb 91 14:40:57 GMT From: ratzan@pilot.njin.net (Lee Ratzan) Subject: modula3 status can anyone inform me of the current status of modula3 in academic/scientific/educational circles? i do not see it being used very much in major new jersey research institutions of all kinds. any ideas? lee ratzan univ of medicine/dentistry of nj ------------------------------------------------------------------------------ Date: 21 Feb 91 16:23:10 GMT From: mfranz@bernina.ethz.ch (Michael Franz) Subject: Re: Oberon for the Macintosh Last Friday I announced the availability of MacOberon 2.0 in this newsgroup. Unfortunately, the software that I made available on our FTP server contained a bug which only manifests itself when running the Macintosh in multi-bit color mode under certain releases of the Macintosh System File. I have no way of knowing WHO has downloaded MacOberon from our server since, so please apologize for my distributing this fix globally. Here is a corrected version of Fonts.Obj. Please replace the defective Fonts.Obj by this file. There is no need to get the whole archive of MacOberon 2.0 again, only this single file must be exchanged. The archive on neptune@inf.ethz.ch was updated to include the correct Fonts.Obj module on Thursday, 21st of February 1991 at 14:00h European Time. Thanks to those who reported the error and my sincere apologies once again. Michael Franz Computersysteme, ETH Zurich, Switzerland --- (This file must be converted with BinHex 4.0) :#8C[ER4c,NpLDJ!Z6f*U,NpL-J%!!!!'i!!!!!$rTD%!!%!!!!$kqKDMiD'&4Qp ZG(-!#!!!!K!!4QpZG!!3#J-!!!!!)!!!!!!!)!)4!%jKE@8!%4%!!!!!EQ&YC3! 4"3!!!#"SC@PRD(3!%38!!!!LE@PZ@!!4"3!!!#4YBAKB!"%&!!!!*QeTEPN!%38 !!!!SE@&i@3!@Sq'KJN4TFh"XBAN!#!!"!K)"4QpZG!!4%J!!!#abBA0dCA)!%J! !!$!-$!!!!!!d!!%#%`"'Efjd4'9cB`!%%!!!!!"%C@CKG@ad!!d,!`!!!!!)!!3 1&!!)EQ&YC3!&%!&8D'Pc!"33%a!-$!%!!!!!!!%#&3&#HA4PF`!8%K8!!!@Q!!! !"!!!!jj0B@0TER4[FfJ!Sq'KJ!!!!b4,CA*ZC@`!Sq'KI3!!!!"%DA0`E'&j!+2 KSB)!!!!!!!!!!!!!!!!!!$`!!!")!!!!9!!!!&`!!!"J!!!!Y!!!!,J!!!6d!!! &TJ!!!!#MiD'&!2m!!%C[ER4c!!!!!!!!!!!!!!!!!!!!!!!!J!!!!*3!!!"J!!! !Z!!!!0!!!!!!8hPZG'&i-6!Z8f0Z,NCZG!!!!!$rrrrrrrrrq!!!!!!!!!!%rrr rrrrrrr3!!!!X!!!!-!!!!%!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!#mm!!!!%%Kj!!!!!Q%!!!SMb2rfJ!"1G8j@rX!Z,J!-@)F#4rrm+'i!#*r(*Nm Y6`!)j%mQh&0(C[SYHIr@J!$rr%UZrraQ!!!+B!!!1'!!!$)SE[rm*Qi!#%S8C`D j#fIiB!+j#fB!!"!JE[rm6Pj1G!!)B!!!$#KZrr`YE!!`rraJ[N*ZrrBSEJ!)2Ll rpNM(($4i!!)'!&m-"J""C3!!4LKZ!!Jq,[rf5-FF0(J!!JB!A``'!&TL!!!X$'i !'2rfE!!!)LKZ!!Jq,[rf5-Fm,[rf8NC(l[lS&l4i!'!!8QlrpQ#L3Qlrm$eZrrE rp#KZ!!Jq,[rd5-F-0!!`H!"P!!"++'i!#$iZrr4)a``d!$Pi!')!!$B-EJ!Brr4 X!!!X2Llrm-rm!!SSEJ!)2#lrp%M''M4S!!*&!2rH4Ac3h%Fp4[r`8Qlrp'#N+'i !#$iZrr4)a``d!#ji!'F!!$Bq,[rf8NG*l[lS'E`!,A!!+'i!#$iZrr4)aa`dH!! #"J"I2LlrpP4(5Hlqk"Q'F!"8E[rf5QlrpQB!!!iJHIkHJ!"1ANjd!!K+E[r`CJ! !#$em!!(rm$iZrrBG4rlS5'lqk%KZrr+T!%TZrr*R!!!89Bmr,[rb2blrm+N#5Kp Q!!!1)(RrZS!!6Pj1G!!)2@lrm[l-2@lrm2l13Qlqd$em!3$qdMem!!(qe$em!!( qeMem!!(qf$em!!(qfMem%M6qh&Q25'lqc+N",9rqb#KZrXJYE!!#rX3JE[l%S#N SE[l%,96q`%(Zrr`LI2qB!!%SEIrm6T3SE[rm*Qi!#(iI'0YAcrrm3Hlrq#*m!!! !!LKYrra1P#KZrr`TE[ri!#`SE[l!*Qlrr$GX!!i!)#KZrX!QE[rm0f`!#!!L+'l q`#CZrr`hE!!'!#3SE[l!2L`!&%4(+'lrr$P(!#BSE[l!*Qlrr$GX!")!+%Ujri# !!'F!!"iSHIrfJ!!QE[rm*f`!-!!`+(Rrm)!!+@lrr!!`+'lq`#CZrrJhE!!1!!3 SE[l!2L`!&%4(+'lrq$P(!!BSE[ri1@lrmJ!)+'lrq$PZrr!!#MiZrr,1I!$r5-G m'1f[2#lrm-am!2p)aRS3kklHKLKZrrJm,!!'h%E-I!$r5-EHKP+(+'lrq#L(+'l q`%RX!"!QE[l!2LX!%0j(5-FX$0b(,8Eqi#KZrX!q,!!%9NIH4dM(,#lqi*b(,8E qj%*ZrrBSE[l!2LlrpVjX!!4Z!!#%2LlrpYj(5-IHV[lN+%Fp92rZ2LlrpYj(5-I HV[lN9)FS4ce8rq`q,[rfhNG)apkZrZ!S4ce8rqSq,[rXRQlrlLKZrrJm,[rf1BG N$!aZrrrrkQB!!")SE[ri2LlrpN*dG!jJ!!!@2LlrkXjm!2mSE[ri2#lrpMQ(C!j 5E[rfB!$rF#"ZrX5J+L"Zrra1ANjd!!K3qJ!46R(i!"JN!!%0!!!!!%4PCQ&eE(3 !q!3k9'KTF`!"$IrrrX4QE@CS!!%0rrrq`'CYCQjd!!%0rrrqb'CYEh9d!!%0rrr rr'CZG!!""Irrrr*QER41G@d!!3Arrrr`CQjd8fPkC3!""IrrrrCT!!%&rrrrp'S !!3ErrrlNE3!"$IrrrrKYBA!!!3ErrrlJEJ!""IrrrqaZCAKdF'pc!!%&rrrrkQp QCPGTC!!""Irrrqj`Eh-!3i)!!!: ------------------------------------------------------------------------------ Date: Thu, 21 Feb 91 19:04:46 -0500 From: Roger Hoover Subject: Problems with beta4 I've been compiling beta4 on a Sparcstation 1 using gcc. 1) builtin_driver_args in m3.c writes into its argument, which can be a string constant. This requires the gcc -fwritable-strings flag. Are writable strings required for the target code as well? 2) I've been getting nondeterministic runtime errors from the m3 compiler. Anyone else see this? They go away if you retry the compile. For example: .ROOT/system/driver/m3.local -D.ROOT/libs/Xlib/Interfaces:.ROOT/libs/Xt/Interfa c es:.ROOT/libs/Xaw/Interfaces -g -c xboxes.m3 M3 runtime error: Value out of range Program .ROOT/system/compiler/m3compiler got fatal signal 3. *** Error code 1 % make demos --- X11R4 starting on Thu Feb 21 18:46:29 EST 1991 ------ examples starting on Thu Feb 21 18:46:31 EST 1991 .ROOT/system/driver/m3.local -D.ROOT/libs/Xlib/Interfaces:.ROOT/libs/Xt/Interfa c es:.ROOT/libs/Xaw/Interfaces -g -c xboxes.m3 .ROOT/system/driver/m3.local -D.ROOT/libs/Xlib/Interfaces:.ROOT/libs/Xt/Interfa c es:.ROOT/libs/Xaw/Interfaces -g -o xboxes xboxes.mo .ROOT/libs/Xaw/libm3Xaw.a . ROOT/libs/Xt/libm3Xt.a .ROOT/libs/Xlib/libm3X11.a /usr/lib/libXaw.a /us r I also got the same error with Xlib/X.i3. Anyone else see this? roger hoover rhoover@ibm.com ------------------------------------------------------------------------------ Date: Thu, 21 Feb 1991 15:23:27 PST From: Mike_Spreitzer.PARC@xerox.com Subject: Re: Closures in m3 [A mailer claimed this didn't make it to M3 the first try...] You are right about the first point: local procedures can't escape (given M3 as it is). So the closure scheme is more convenient in some cases. It's less convenient in others (where the local procedure would work, making the local procedure involves less overhead than making a whole closure object). Wouldn't the local procedure scheme suffice in the vast majority of actual uses? Note that if there were a syntax for objection creation that allowed method literals, the closure scheme can also approach the elegance of CLU iterators. In clarification of my comment about assigning local procedures, I meant that I don't see why it isn't equally as possible as dereferencing UNTRACED references --- that is, something that you can do in an unsafe module when you know the referenced locations (whether on the stack or in the heap) are still being used as you expect. I was not proposing this as a way to change the fact that local procedures can't escape. To do that would require changes that I expect the language designers will say are too pervasive, expensive, difficult to implement, or some combination thereof. Mike ------------------------------------------------------------------------------ Date: Fri, 22 Feb 91 21:01:48 JST From: kagotani@cs.titech.ac.jp Subject: GNU Make problem with 1.6beta4 I am using GNU Make to build SRC Modula-3 1.6beta and it stops when compiling tools/coverage. /bin/make does not stop but complains that the Makefile is inconsistent. Here is the fix of util/m3all.tmpl file. *** dist-1.6beta4/util/m3all.tmpl Thu Feb 14 11:19:20 1991 --- new/util/m3all.tmpl Fri Feb 22 14:12:55 1991 *************** *** 76,82 **** #define C_program(n) @ @\ all:: n @ @\ ! n:: n.c ; $(CC) $(CFLAGS) $(COPT) -D$(MACHINE) -o n n.c $(CLINKFILES) #define M3_program(name,libs) @ @\ implementations:: name @ @\ --- 76,82 ---- #define C_program(n) @ @\ all:: n @ @\ ! n: n.c ; $(CC) $(CFLAGS) $(COPT) -D$(MACHINE) -o n n.c $(CLINKFILES) #define M3_program(name,libs) @ @\ implementations:: name @ @\ -- Hiroto Kagotani kagotani@cs.titech.ac.jp ------------------------------------------------------------------------------ Date: 22 Feb 91 15:07:01 GMT From: wilson@uicbert.eecs.uic.edu (Paul Wilson) Subject: Re: Closures in m3 Mike_Spreitzer.PARC@xerox.com writes: >[A mailer claimed this didn't make it to M3 the first try...] >You are right about the first point: local procedures can't escape (given M3 a s >it is). So the closure scheme is more convenient in some cases. It's less >convenient in others (where the local procedure would work, making the local >procedure involves less overhead than making a whole closure object). Wouldn' t >the local procedure scheme suffice in the vast majority of actual uses? A moderately smart compiler can detect most cases where a procedure can't escape, and compile it the cheap way. The T and Gambit compilers do this for Scheme. I don't see why it wouldn't work for M3. (One really obvious case is to avoid making a closure for any procedure which is only called, without ever creating a pointer to it. That gets rid of most closures right there. >In clarification of my comment about assigning local procedures, I meant that I >don't see why it isn't equally as possible as dereferencing UNTRACED reference s >--- that is, something that you can do in an unsafe module when you know the >referenced locations (whether on the stack or in the heap) are still being use d >as you expect. I was not proposing this as a way to change the fact that loca l >procedures can't escape. To do that would require changes that I expect the >language designers will say are too pervasive, expensive, difficult to >implement, or some combination thereof. Full closures aren't terribly expensive if you optimize away the obvious ones. Being careless about compiling control structures like generators can cause a lot of closure-creation, though, so that heap allocation is significantly increased. Depending on how good a garbage collector you've got, that may be a factor. If you've got a generational gc and do the obvious optimizations on closures, though, you should be okay. (One thing David Kranz' thesis from Yale showed was that closures don't have to cost anything to programs that don't use them. So you may not want to make this stuff unsafe, because the cost of doing it right may not be all that much.) > Mike -- Paul Paul R. Wilson Software Systems Laboratory lab ph.: (312) 996-9216 U. of Illin. at C. EECS Dept. (M/C 154) wilson@bert.eecs.uic.edu Box 4348 Chicago,IL 60680 -- Paul R. Wilson Software Systems Laboratory lab ph.: (312) 996-9216 U. of Illin. at C. EECS Dept. (M/C 154) wilson@bert.eecs.uic.edu Box 4348 Chicago,IL 60680 ------------------------------------------------------------------------------ Date: Fri, 22 Feb 91 11:05:33 PST From: muller@src.dec.com (Eric Muller) Subject: Re: Problems with beta4 In article <9102220004.AA28265@cirrus.watson.ibm.com>, Roger Hoover reports some problems with 1.6beta4: > 1) builtin_driver_args in m3.c writes into its argument, which > can be a string constant. This requires the gcc > -fwritable-strings flag. Are writable strings required for the > target code as well? I fixed that routine so that it doesn't write into its argument. I am not sure about generated code, but I would guess that we don't try to modify it. > 2) I've been getting nondeterministic runtime errors from the m3 > compiler. I haven't seen this (but we don't have Sparcstations). Looks like an uninitialized variable somewhere, but where ? Eric. ------------------------------------------------------------------------------ Date: Fri, 22 Feb 91 12:19:17 PST From: muller@src.dec.com (Eric Muller) Subject: Re: GNU Make problem with 1.6beta4 In article <9102221202.AA25225@sami.cs.titech.ac.jp>, kagotani@cs.titech.ac.jp writes: > I am using GNU Make to build SRC Modula-3 1.6beta and it stops when > compiling tools/coverage. /bin/make does not stop but complains that > the Makefile is inconsistent. Fixed. Thanks. -- Eric. ------------------------------------------------------------------------------ Date: Fri, 22 Feb 91 23:07:29 GMT From: craig@Neon.Stanford.EDU (Craig D. Chambers) Subject: Re: Closures in m3 In article <1991Feb22.150701.15020@uicbert.eecs.uic.edu> wilson@uicbert.eecs.ui c.edu (Paul Wilson) writes: >A moderately smart compiler can detect most cases where a procedure can't >escape, and compile it the cheap way. The T and Gambit compilers do this >for Scheme. I don't see why it wouldn't work for M3. (One really obvious >case is to avoid making a closure for any procedure which is only called, >without ever creating a pointer to it. That gets rid of most closures >right there. > >... > >Full closures aren't terribly expensive if you optimize away the obvious >ones. Being careless about compiling control structures like generators >can cause a lot of closure-creation, though, so that heap allocation >is significantly increased. Depending on how good a garbage collector >you've got, that may be a factor. If you've got a generational gc and >do the obvious optimizations on closures, though, you should be okay. >(One thing David Kranz' thesis from Yale showed was that closures don't >have to cost anything to programs that don't use them. So you may not >want to make this stuff unsafe, because the cost of doing it right >may not be all that much.) Closures can be optimized away as you describe only if the control structure procedures that eventually call the closure all get inlined into the function creating the closure. Kranz' work on eliminating closures that are only called applies only to closures defined and invoked locally (within a single procedure). This is presumably not the case with the list iterator example being discussed. Another optimization described in Kranz' work requires interprocedural analysis to detect when a procedure never stores one of its arguments into the heap or passes the argument to a function that might do such a store, i.e. detected which parameters to functions are only ever passed down or invoked but never "escape" as Kranz calls it. For closures passed in non-escaping argument positions to non-inlined functions, the compiler can use a cheaper (e.g. stack-allocated) representation of the closure since the compiler has proven that the block can never escape upwards. I don't know how often this analysis succeeds in being effective. I'd expect the job to be much harder in an object-oriented language than a procedural language like Scheme, since the interprocedural analysis is more complicated. -- Craig Chambers ------------------------------------------------------------------------------ Date: Fri, 22 Feb 91 23:24:18 GMT From: craig@Neon.Stanford.EDU (Craig D. Chambers) Subject: Re: Closures in m3 In article <91Feb21.152416pst.16910@alpha.xerox.com> Mike_Spreitzer.PARC@xerox. com writes: >You are right about the first point: local procedures can't escape (given M3 a s >it is). So the closure scheme is more convenient in some cases. It's less >convenient in others (where the local procedure would work, making the local >procedure involves less overhead than making a whole closure object). Wouldn' t >the local procedure scheme suffice in the vast majority of actual uses? Note >that if there >were a syntax for objection creation that allowed method literals, the closure >scheme can also approach the elegance of CLU iterators. In fact, full closures (or even downwards-only closures) are *more* powerful than CLU iterators (if this is what you meant by "elegant"). CLU iterators effectively take a single closure argument (the body of the for loop). If closures are first-class, then iterators can take multiple closure arguments. In our Self system we have many methods that take multiple closure arguments. For example, one iterator method called "doFirst:Middle:Last:" takes a closure to execute for the first element of the receiver collection, a different closure for the interior elements, and a third closure for the last element of the collection. This is useful for instance when constructing a string representation of the collection and needing to insert commas in between elements; the code can insert a comma before all the elements except the first by using a different closure for the first element and the other elements. One thing that makes closures usable in practice is a concise closure literal syntax. Since Self and Smalltalk both rely on closures and messages for all control structures, even ifs and whiles, this concise syntax had to be developed just to allow the language to be used at all. Without it, I wouldn't expect closures to receive much use. This lack of concise syntax in Pascal-like languages has probably been a major hurdle to widespread use of closures. -- Craig Chambers ------------------------------------------------------------------------------ Date: 22 Feb 91 17:06:42 GMT From: C.R.Snow@newcastle.ac.uk (Dick Snow) Subject: Modula-3 on the Encore Multimax Modula-3 has now been ported succesfully to an Encore Multimax machine running UMAX (a close relative of Unix BSD4.3). Readers familiar with this machine will know that it is a shared memory multiprocessor machine in which UMAX allocates processors to Unix processes. The Multimax implementation of Modula-3 is derived in a fairly straightforward way from the VAX implementation, and runs within a single Unix process. The Threads package also runs, but again only pseudo-concurrently within a single process. We are looking into ways in which the Threads package can be re-implemented to exploit the true parallelism of the Multimax. Dick Snow & Robert Stroud. ------------------------------------------------------------------------------ Date: 28 Feb 91 15:41:02 GMT From: JFS10@psuvm.psu.edu (Jonathan Swaby) Subject: Oberon or Modula-3 Amiga versions ? Are modula-3 or Oberon available for the Amiga? If so where can one ftp them from? Jonathan Swaby ------------------------------------------------------------------------------ Date: 28 Feb 91 19:18:59 GMT From: andy@jarthur.Claremont.EDU (Andy Gray) Subject: Oberon or Modula-3 for the Atari ST ? In article <91059.104102JFS10@psuvm.psu.edu> JFS10@psuvm.psu.edu (Jonathan Swab y) writes: >Are modula-3 or Oberon available for the Amiga? If so where can one >ftp them from? What about the Atari ST? Is anyone aware of a Modula-3 or an Oberon compiler for the ST? :-) to Cliffie... -- Andy ---------------------------------------------------------------------------- Andy Gray andy@jarthur.claremont.edu Platt Campus Center andy@jarthur.UUCP Harvey Mudd College ...uunet!jarthur!andy Claremont, CA 91711 agray@HMCVAX.BITNET ------------------------------------------------------------------------------