Path: utzoo!attcan!uunet!aplcen!samsung!brutus.cs.uiuc.edu!psuvax1!psuvm!odx From: ODX@PSUVM.BITNET (Tim Larson) Newsgroups: comp.lang.modula2 Subject: The WITH statement Message-ID: <89321.154018ODX@PSUVM.BITNET> Date: 17 Nov 89 20:40:18 GMT Organization: Penn State University Lines: 86 (* 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. *)