7
votes

Most Pascal control structures make sense to me, like:

for ... do {statement};

if (condition) then {statement};

while (condition) do {statement};

where the {statement} is either a single statement, or a begin ... end block. I have a problem with:

repeat {statement-list} until (expression);

try {statement-list} except {statement-list} end;

Wouldn't it be better that repeat and try have the same general structure, accepting only a single statement or a begin ... end block, instead of having a statement-list that's not formally blocked with a begin and an end?

7
I've made the mistake of asking two questions here - "Why are pascal control structures inconsistent?", then in the body of the text, asking "Wouldn't it be better?". Now that there are excellent answers to both, do I edit this and create another question for the other answer? What's the preferred StackOverflow way of dealing with this? - 70Mike
you are completely missing compound statement concept, which is zero or more statements surrounded by begin..end - Free Consulting
@Mike: you seem to ask this because of a code formatter; maybe you want to extend this question (stackoverflow.com/questions/402737/delphi-code-formatter) or ask a new question about a code formatter that supports new language features (class vars, class consts, generics) well. - Jeroen Wiert Pluimers
@70Mike As Greg Hewgill points out, this is something that Wirth corrected in Modula-2 and all his subsequent languages. I believe the the single/compound statement distinction, a carry over from Algol, is one his biggest regrets over Pascal. The inconsistency of single/compound statement leads to programmer errors. The simple clarity in Modula-2 is the right solution to the problem. - David Heffernan
@70Mike (continued) My company's product was formerly implemented in Modula-2 using the TopSpeed compiler which later became Clarion. When we ported to Delphi (out of necessity due to lack of support from TopSpeed) we introduced coding standards that banned the use of single statements. All our if/for/while statements have begin/end blocks. I'm convinced that this was an excellent decision and the rule remains in place today. - David Heffernan

7 Answers

9
votes

The forms that require a begin/end all exist on a single line--the compiler has no other way to know where the block ends. The forms that don't require a begin/end have a closing structure that tells the compiler where it ends and thus the begin/end would simply be redundant. You're free to use it in this case if you want, though.

8
votes

Niklaus Wirth (the designer of Pascal) corrected these inconsistences in his next language, Modula-2. In Modula-2, compound statements like IF have no BEGIN and a mandatory END:

IF {condition} THEN
    {statement-list}
END;

Other control structures such as WHILE are similar and consistent with REPEAT.

5
votes

The question is: Wouldn't it be better ?

The answer is: That depends, as this sort of thing is entirely subjective. Unless you are, or think like, a machine.

Yes, it would satisfy some ache for consistency to enforce begin/end for ALL compound statements, but where the surrounding language elements already provide a natural enclosure, it is utterly redundant to require this.

Consider the CASE statement:

// "Sensible" syntax

  case VALUE of
    1: DoSomething;
    2: begin
         DoSomethingElse;
         DoMore;
       end;
  else
    DoForAllOtherValues;
    DoMore;
  end;

Versus a less sensible but more consistent and "logical":

  case VALUE of
    1: DoSomething;
    2: begin
         DoSomethingElse;
         DoMore;
       end;
  else
    begin
      DoForAllOtherValues;
      DoMore;
    end;
  end;

Notice that the final "end" is part of the "case". You cannot do without it.

I am fairly sure that in an early version of Chrome (that became Oxygene and subsequently Prism) this was actually required syntax for the case statement. If so, it is no longer the case. Common sense presumably prevailed.

In my personal opinion, satisfying the OGoSC (Objective Gods of Syntactic Consistency) angers the perhaps lesser, but actually more relevant to you and me, SGoHRaC (Subjective Gods of Human Readability and Comprehension).

Though in many cases it might appear otherwise, we humans are not in fact machines. We do not need to simplify and reduce rules to a minimum consistent set to make it possible to parse text and make sense of it. We need some rules, but we can handle more, since our great advantage over machines is a freedom of thought that liberates us from a strict regimen of syntax and structure, especially where such syntax and structure is extraneous to the point of redundancy.

As in this case.

If you make a mistake that the compiler cannot interpret, it will tell you, every time you compile. But the compiler won't thank you for making the code "easier" to "read" (the compiler simply follows the rules it is given - it does not make it "easier" for the compiler to "read" the code by changing the rules that it can already follow perfectly happily).

If you impose arbitrary rules that make it harder to read (not because the rules are more or less invariant/consistent, but because you impose a consistent structure that itself contains more noise and redundant information that has to be filtered) then you will pay the price in human productivity.

In fact, these "easier" more "consistent" rules, may actually may make it harder all around ... consider again the CASE statement.

To make that compound begin/end make sense, we must make "case" a standalone statement, not part of a case/end pair, thus ALL of these should be valid syntax:

  case VALUE of
    1: DoSomething;
    2: DoSomethingElse;


  case VALUE of
    1: DoSomething;
    2: DoSomethingElse;
  else
    DoOther;


  case VALUE of
    1: DoSomething;
    2: begin
         DoSomethingElse;
         DoMore;
       end;
  else
    DoOther;


  case VALUE of
    1: DoSomething;
    2: begin
         DoSomethingElse;
         DoMore;
       end;
  else
  begin
    DoOther;
    DoMoreOther;
  end;


  case VALUE of
    1: DoSomething;
    2: begin
         DoSomethingElse;
         DoMore;
       end;

You may disagree, but it seems to me that suddenly this more consistent syntax results actually in LESS consistency in the actual code, even though there is greater consistency in the rules that the code is being written to conform to.

3
votes

Probably. But language design, especially languages with a bit of history, is rarely straightforward or ideal.

You can see similar things in other languages. Java, for example, requires a block after try and won't allow a single statement, although a single statement might also work if you just look at other control structures.

Why is switch in C and derived languages a single block and the individual cases not?

2
votes

It has to do with the way the parser works. Begin/end, try/except and repeat/until all contain blocks of statements. The parser's code looks something like this, for a begin block (pseudocode):

procedure ReadBlock;
begin
  Match('begin');
  while CurrentToken <> 'end' do
    ReadStatement;
  Match('end');
end;

And that works fine for that type of block. But when you need additional information in the block ending line (a conditional for the repeat/until, and an exception-handling block for the try,) then you don't want it to run until end, which the language's grammar expects to not have anything after it. You could modify the grammar, but that would add a lot of complexity to the parser. So instead you just pick a different keyword.

1
votes

I guess you are asking the wrong question. I mean, may be you are not seeing the difference between the if/while/for group and the repeat/try group.

The first group needs something (condition or arguments) to BEGIN something. The second, instead, implies to BEGIN something. Just read the words: repeat (the following) and try (the following).

Pascal is clean, simple and elegant because it's designed for regular human readers, Professor Wirth had in mind people learning programming when he designed it.

1
votes

You are partly correct - on the repeat <stmts> until <expr> statement.

It feels a bit of a cheat that you don't need BEGIN-END for compound statement consistency but this is to avoid undue suffering. Because you see in all other cases you need the begin-end bracketing effect to show where the part of the code to which then or else or loops do apply to. REPEAT-UNTIL is the only Pascal statement i can think of that necessitates a far-removed second part - because it makes sense the condition for loop exit to be textually where it will be checked - after the loop body. An alternative form could have been

UNTIL <condition> DO <statement>

UNTIL <condition> DO
  BEGIN
    <statements>
  END

but then it would be way too confusing to explain and persuade the language users that the condition will be checked only after the block is executed at least once. So it's a good thing Wirth did not do it this way.

In regards to try <stmts> except <stmts> end - this is not from the original Pascal language design and specification, this has been slapped in as after-thought by some implementor (Borland? FreePascal?), so it's no wonder it is inconsistent - the thought has been more to the lines of "can i do it without breaking existing programs" than overall specification design. Another example of language extension was given in answer above - the default clause else in the case switch - and while i find this very useful and had used it back in the days in Turbo Pascal - this is horribly inconsistent with the use of else in the if construct. Thus i find another implementation i have seen and used better - otherwise: followed by a single statement as in each <value>: case.