new@ee.udel.edu (Darren New) (02/08/91)
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 ... +=+=+=
gnelson (Greg Nelson) (02/08/91)
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
bbc@rice.edu (Benjamin Chase) (02/08/91)
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 <bbc@rice.edu>, Rice University, Houston, Texas
muller@src.dec.com (Eric Muller) (02/09/91)
In article <BBC.91Feb8074227@sicilia.rice.edu>, 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.