On control structures and expressions

Ursula Dreier Ursula.Dreier@ruhr-uni-bochum.de
Sat, 28 Nov 1998 23:21:59 +0100


Variables - a long time ago I read an article from one of the PL gurus
(on Algol, I think) containing the sentence "variables considered
harmful". I think this is true, but unfortunately we can hardly do
without them. But trying to avoid the need to use temporary variables
should certainly be an issue. Make heavy use of expressions instead. Let
me explain using one of the ugliest examples I can think of:

int GetWindowText (HWND hWnd, LPTSTR lpString, int nMaxCount);

a) This thing returns true (actually the length of the string) almost
all the time, rendering the precious return value useless for all
practical purposes.
b) It requires that you allocate storage for that text, having to guess
how long it may grow. You have to think of a name for the storage. Maybe
you are lazy and call it sTemp or sScr or ptr or just s. Maybe you just
carelessly allocate or reuse a pointer without providing the storage,
and be rewarded with an exception if you are lucky.
c) You have to specify a correct length as an additional parameter. If
you make a mistake there...(see above).
d) Now that temporary variable is laying around. Is the value being
reused later on? You can't be sure unless you examine the whole scope.
If this thing occured in the "then"-part of an if, what happens on
"else"? More uncertainty. Maybe you also reuse the tempvar for another
purpose later on, what about (lack of) initialisation then?
e) What happens if GetWindowText does return false?
Because you were in a hurry (or lazy, like me, or both), you forgot to
code the error handling for this case. No one reminds you of that if you
don't put in a comment with "TODO" to be searched later on.

A better approach would be to have a function...

string GetWindowText (windowHandle hWnd)

...and to use the return value in an EXPRESSION.
If the function can't return a default or special value on error, an
exception should be raised and the compiler should provide information
about that fact to you and warn you, if you choose (much like the way it
is done in Java).


A lesson to be learned from this example is: Return values should be
used for the result, not for some state information or error code.


There are programming languages where every construct yields a value,
even loops.
The usual semicolon at the end of a "statement" is actually an infix
operator with a low binding power, which evaluates its left argument,
then discards it and evaluates and yields its right argument.

This allows for constructions like these:

5+(doSomething(); b)
if a then b else c fi := if d then e else f fi;

Advantages:
+ concise, short
+ avoids need for temp vars

Drawbacks
- reduced locality (tempting to write very long and complicated
expressions)
- compiler has to check for compatible types of intermediate results at
places where control flow converges (end of if clause, end of loop
clause). Especially complicated when else is missing.

Maybe it will be worthwile to discuss how far to carry that sort of
expression orientation.


I got a question regarding back-translation of a parse tree to some
user's preferred notation: How about layout and comments? Especially
layout is specific to a certain notation and will be lost during
transformation to a parse tree.


Some other (mixed) ideas:

 How about having one general loop construct like this:

do
 while <condition>; // Omit this if you don't want a pre-tested loop
 // (do something)
 while <condition>; // insert wherever you want to test in the middle of
a loop
 // (do something)
 while <condition>; // Omit this if you don't want a post-tested loop
od    // or call it endLoop, whatever you like more

while <condition>; would be a shorthand for: if <condition> then break
fi;
until <condition>; would be a shorthand for: if not (<condition>) then
break fi;

There a only two concepts here: The endless loop do ... od and a general
break condition test which has the same structure as any other
statement.
At runtime, things occur strictly in the order they are written (while &
until are prefix operators).


Some duplicate code could be avoided if it occurs in places like this:

if a
 then b;
 x;  // duplicate
else if c
 then d;
 x;  // duplicate
else e;
fi


In that language of my own design there was a construction like this:

if (a)
 then b;
else if (c)
 then d;
elfi x;
else e;
fi

The semantics is as follows: When encountering an "else" during
execution of the preceding (then-)clause (b, in this case), jump to the
next matching fi OR elfi, whatever is nearer.


Hans-Dieter Dreier