ODX@PSUVM.BITNET (Tim Larson) (11/18/89)
(* Good grief! I was away from my debugger yesterday and I stared at a piece of code for almost an hour before I discovered the culprit - a WITH statement! I don't believe I have ever used the statement in the offensive way I wrote it, but, on the other hand, I never knew I couldn't until now. The code in question is too large to post here, but it was essentially equivalent to: WITH A[i] DO REPEAT WrInt(f, -1); INC (i) UNTIL i > 3 END; (see the program following this message) which I intended to mean: REPEAT WrInt(A[i].f, -1); INC (i) UNTIL i > 3; I believe that the WITH statement as defined in Modula-2 is a bad idea. Informally, we often think of the WITH statement as a way of reducing the size of the source code, and that we may simply replace the field names within the WITH block by the name in the WITH designator. This is simply not true. What is really happening is that the designator is bound once when the WITH statement is encountered and the field names are then treated as fields of this static object. In the test code given below, the mistake is quite obvious. This is not always the case, however, and it IS the responsibility of the language designer to provide semantics that correspond in an obvious way with the syntax. The WITH designator is not required to be a static object and yet it is treated as if it were static. This is a mistake! In any case, since Modula-2 is a good language in so many respects, and assuming that the semantics will not now be changed, the compiler should be made to produce a warning at the least, or perhaps even an error message if the designator's value changes in the body of a WITH block. There is a test program below that illustrates the comments above. Any comments about this? *) MODULE TestWITH; FROM IO IMPORT WrInt, WrLn; TYPE R = RECORD f: INTEGER END; VAR A: ARRAY [0..3] OF R; p: POINTER TO R; i: INTEGER; BEGIN (* Note that the desired output is 1234. *) A[0].f := 0; A[1].f := 1; A[2].f := 2; A[3].f := 3; (* The following two blocks are essentially equivalent in the way they execute. *) i := 0; WITH A[i] DO REPEAT WrInt (f, -1); INC (i) UNTIL i > 3 END; WrLn; i := 0; p := ADR(A[i]); REPEAT WrInt (p:.f, -1); INC (i) UNTIL i > 3; WrLn; (* The informal way of thinking of WITH leads one to believe that the first block is equivalent to the following block (which represents the code that was intended). *) i := 0; REPEAT WrInt (A[i].f, -1); INC (i) UNTIL i > 3; WrLn; END TestWITH. (* -Tim Larson odx@psuvm.bitnet Naturally, these opinions, for what they are worth, are just mine. *)
jbaker@gmu90x.gmu.edu (jbaker) (11/20/89)
In article <89321.154018ODX@PSUVM.BITNET> ODX@PSUVM.BITNET (Tim Larson) writes:
. couldn't until now. The code in question is too large to post here,
. but it was essentially equivalent to:
.
. WITH A[i] DO REPEAT WrInt(f, -1); INC (i) UNTIL i > 3 END;
.
. (see the program following this message) which I intended to mean:
.
. REPEAT WrInt(A[i].f, -1); INC (i) UNTIL i > 3;
.
. I believe that the WITH statement as defined in Modula-2 is a bad idea.
. Informally, we often think of the WITH statement as a way of reducing
. the size of the source code, and that we may simply replace the field
. names within the WITH block by the name in the WITH designator. This
I always thought of a WITH statement differently. It computes the location
of the record just once, freezes it, and uses that record throughout the
WITH statement. Thus, not only does it reduce source code size, it also
increases program efficiency.
. In any case, since Modula-2 is a good language in so many respects, and
. assuming that the semantics will not now be changed, the compiler should
. be made to produce a warning at the least, or perhaps even an error
. message if the designator's value changes in the body of a WITH block.
A warning might be a good idea - but remember, it may happen frequently that
correct code would recieve a warning. No, forget the warning. When
looking at a WITH statement, you should always go to the WITH to find out
which record is being referred to - and remember that any computations
were made when the WITH was entered.
John Baker
Fairfax, Virginia
ODX@PSUVM.BITNET (Tim Larson) (11/20/89)
In article <2353@gmu90x.gmu.edu>, jbaker@gmu90x.gmu.edu (jbaker) says: > >I always thought of a WITH statement differently. It computes the location >of the record just once, freezes it, and uses that record throughout the >WITH statement. Thus, not only does it reduce source code size, it also >increases program efficiency. > This is a good point, it *should* increase efficiency, but it also may not! A good compiler optimizes code for the programmer, but WITH broke the optimizer in mine (JPI) and I don't believe it's the compiler's fault. To maintain correctness, it had to do the computation for the WITH statement, then use that computation to reference the record. When I wrote the same code and removed the WITH, the code size and execution time was reduced! The reason is that the compiler was then freed to optimize the record ref. to registers and reduce the code size. (Of course, this type of optimization is specific to PCs.) The upshot is, the optimization forced on the programmer by remembering the subtleties of the WITH statement is overshadowed by the potential of a good optimizing compiler, and the potential cost is correctness! It's interesting to note that JPI's manual, when mentioning WITH, mildly discourages its use. -Tim Larson odx@psuvm.bitnet
R_Tim_Coslet@cup.portal.com (11/24/89)
>In Article: <89321.154018ODX@PSUVM.BITNET> > ODX@PSUVM.BITNET (Tim Larson) Wrote... > > WITH A[i] DO REPEAT WrInt(f, -1); INC (i) UNTIL i > 3 END; > > (see the program [deleted] following this message) which I intended to mean : > > REPEAT WrInt(A[i].f, -1); INC (i) UNTIL i > 3; > I think the code you REALLY wanted was... REPEAT WITH A[i] DO WrInt(f, -1); INC (i) END UNTIL i > 3; > I believe that the WITH statement as defined in Modula-2 is a bad idea. I believe that if you use it right, there are no statements in Modula-2 that are a "bad idea"! It is the responsibility of the programmer to understand the tools (i.e. statements) provided to him by the language (i.e. Modula-2) or environment (i.e. Operating System) that he is working with. As I see it, if he then uses those tools wrong and the program breaks... it is as much his problem as it would be the problem of a carpenter that chooses to use screwdrivers as chisels and the wood he is working on breaks. In both cases I would consider the user (programmer or carpenter) of the tool (WITH statement or screwdriver) not the supplier of the tool to be the cause of the problem (because he "misused" the tools). R. Tim Coslet Usenet: R_Tim_Coslet@cup.portal.com BIX: r.tim_coslet
ODX@PSUVM.BITNET (Tim Larson) (11/27/89)
In article <24410@cup.portal.com>, R_Tim_Coslet@cup.portal.com says: >I believe that if you use it right, there are no statements in Modula-2 that >are a "bad idea"! > >It is the responsibility of the programmer to understand the tools (i.e. >statements) provided to him by the language (i.e. Modula-2) or environment [...] >In both cases I would consider the user (programmer or carpenter) of the >tool (WITH statement or screwdriver) not the supplier of the tool to be >the cause of the problem (because he "misused" the tools). I agree with you that a programmer, like any craftsman, must know his tools intimately, and use them properly. However, any tool can be improved; after all, Modula-2 is an improvement upon older tools, such as Pascal. Who better than the craftsmen to suggest improvements? Who better than the designer to implement the improvement? Stone axes were once state-of-the-art, till someone challenged the prevailing wisdom. -Tim Larson odx@psuvm.bitnet