Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!swrinde!elroy.jpl.nasa.gov!decwrl!pa.dec.com!src.dec.com!gnelson From: gnelson (Greg Nelson) Newsgroups: comp.lang.modula3 Subject: Re: Questions from a new advocate of Modula-3 Message-ID: <9102080401.AA29237@jumbo.pa.dec.com> Date: 8 Feb 91 04:00:50 GMT Lines: 136 In-Reply-To: Message of 7 Feb 91 21:10:46 GMT from new@ee.udel.edu (Darren New) <44046@nigel.ee.udel.edu> To: m3 X-Folder-Carbon: sent 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