22
$\begingroup$

In my programming language, AEC, I am using keywords EndIf and EndWhile with the same meaning as in SmallBasic, and I am also using EndFunction (corresponding to EndSub in SmallBasic) and EndStructure (for ending the structures). But I have noticed very few languages use different tokens for those things. Of the programming languages I know, only SmallBasic, Ada and VHDL (with a syntax based on Ada) distinguish those things. Most programming languages I know use } for all of those things, and those that do not (such as Pascal or MatLab or Lua) use end for all of those. Why?

An obvious answer is that using the same token for all those things makes the programming language slightly easier to parse. But I think that cannot be the main reason. Furthermore, I think that Lua is even more difficult to parse because it uses end for all those things, but doesn't use begin (like Pascal does). When parsing Lua, you basically need to keep track of all do-es and then-es at the same time. Whereas, when parsing AEC, you can first deal with Then-es and EndIf-es, and then, after you have done that, deal with Loop-es and EndWhile-es.

The other obvious answer is that } is easier to type than EndIf. But I am also not sure that's the case. I am Croatian, and I don't even know where } is on the Croatian keyboard. If I were forced to use the Croatian keyboard, it would be much easier for me to type EndIf than to type }.

I think using different tokens for EndIf and EndWhile makes the code significantly easier to read by humans. But maybe I am biased, since SmallBasic was my first programming language. What do you think?

$\endgroup$
13
  • 10
    $\begingroup$ The obvious answer to me would be that End is easier to type than EndFunction or EndStructure. Another useful point of comparison might be HTML/XML, where each opening tag must be matched by a closing tag which includes the tag's name; I think many people don't like editing HTML or XML by hand, because it's so verbose. $\endgroup$
    – kaya3
    Commented Jul 3, 2023 at 0:16
  • 6
    $\begingroup$ See also TeX \begin{foo}\end{foo}. $\endgroup$
    – Bbrk24
    Commented Jul 3, 2023 at 1:11
  • 2
    $\begingroup$ By the way, a quick google says that on Croatian keyboards you should be able to easily type { with either Ctrl + Alt + B or AltGr + B. (AltGr is the right Alt key). And with N for } $\endgroup$
    – Ivo
    Commented Jul 3, 2023 at 9:10
  • 4
    $\begingroup$ "...very few languages use the same token for all those things." I think you mean "very few languages use different tokens for all those things." $\endgroup$ Commented Jul 3, 2023 at 9:52
  • 4
    $\begingroup$ Regarding the Croatian keyboard and ease of typing. Regrettably, I think that is rarely if ever a consideration for those designing languages. Nearly all languages restrict themselves to characters that are easy to type on old US keyboards and have little other consideration. $\endgroup$ Commented Jul 3, 2023 at 10:01

8 Answers 8

36
$\begingroup$

There are four broad approaches on how to put a block of code inside a control structure in a block-structured language.

The Algol Way

In the Algol way, each control structure has its own way to "close" the body. In Algol, specifically, you spell the opening word backwards:

IF  3 = input THEN
   # Do something #
FI;

CASE input IN
    ~ # Do something, because input = 1 #
  ,
    ~ # Do something, because input = 2 #
  ,
    ~ # Do something, because input = 3 #
  ,
    # fall through to the next statement IF there is no "OUT" #
  OUT
    ~ # Do something ELSE. #
ESAC

Fortran uses something similar, using the keyword end followed by the type of construct that it ends:

IF logical-expression THEN
   statements-1
ELSE
   statements-2
END IF

The Pascal Way

In the Pascal way, the body of a control structure is a single statement. Note that there is no "endif" token as part of the control structure's syntax:

if logical-expression then
    single-statement-1
else
    single-statement-2

However, a statement can be a block. In Pascal, blocks are denoted by begin and end. So the end token delimits the block, not the if statement which contains the block.

if logical-expression then
  begin
    statements-1
  end
else
  begin
    statements-2
  end

C also uses this syntax, but blocks are denoted by { and }.

if (logical-expression)
    statement-1
else
    statement-2

if (logical-expression) {
    statements-1
} else {
    statements-2
}

Note that C does not have a then keyword, and so conditions are surrounded by parentheses to ensure that the condition is separate from the body.

The Perl Way

In C, the parentheses around the logical expression are not part of the expression's syntax, they are part of the control structure's syntax. Similarly, you can make the block begin/end markers part of the control structure. This is how Perl does it.

An if-then-else statement in Perl has this syntax:

if (logical-condition) {
   statements-1
} else {
   statements-2
}

Even if you only want a single statement in the body of an if-then-else, the braces must be included, because they are part of the if statement's syntax. The same is true in Rust, where braces are a required part of the syntax for if, while and some other control-flow statements.

However, Perl does allow you to omit braces for a single statement if you use a "reversed" syntax:

# This IS NOT valid Perl
if (x < 2) do_something();

# This IS valid Perl.
do_something()  if x < 2;

Perl's approach has a very counter-intuitive implication. Perl has the control statements next, last, and redo which let you jump around a loop similar to break in C.

while (<STDIN>) {
    # This statement compares the line read from STDIN against
    # the regular expression /^$/. If it matches, exit the loop.
    last  if /^$/;
    statements
}

However, this last keyword only applies to loops. You can't jump to the end of the body of a then:

if (x < 2) {
    some statements
    # The following statement jumps to the end of any
    # enclosing loop, not to the end of the if statement.
    last  if y > 3;
    some more statements
}

You can get the effect you want by naming a block:

LABEL: {
    if (x < 2) {
        some statements
        # The following statement jumps to the end of the
        # named block.
        last LABEL  if y > 3;
        some more statements
    }
}

However, in this case, you don't need the labels. A pair of curly braces that are not part of a control statement (i.e. just a block) has the semantics of a loop that executes exactly once. So either of these will also work:

# This outer pair of braces is, semantically, a run-once loop.
{
    if (x < 2) {
        some statements
        # This statement jumps to the end of the enclosing loop.
        last  if y > 3;
        some more statements
    }
}

# The same as above, only the run-once loop is inside the "then" part.
if (x < 2) {{
    some statements
    last  if y > 3;
    some more statements
}}

I don't know of any other languages which have gone with Perl's approach. It seems a little difficult to explain.

The Occam Way

Occam uses whitespace to denote blocks.

IF
  x > y
    order := gt
  x < y
    order := lt
  TRUE
    order := eq

Python's syntax takes cues from Occam, but be aware that Python is not a block-structured language in the usual sense of that term; blocks are not variable scopes in Python.

$\endgroup$
18
  • 7
    $\begingroup$ This is a great answer on approaches to block structure syntax but does not answer the main question of why most languages use a generic token for ending blocks. $\endgroup$
    – kouta-kun
    Commented Jul 3, 2023 at 4:18
  • 11
    $\begingroup$ @kouta-kun I'm not sure that "most languages" do. But the underlying point is that if you are a block-structured language, you can either come up with one lexical syntax for delimiting blocks, or a different one for each kind of block. If you're going to pick the former, it's inevitably either going to be words like begin/end, or some syntax that resembles bracketing (i.e. [ ], ( ), < >, or { }). $\endgroup$
    – Pseudonym
    Commented Jul 3, 2023 at 5:33
  • 3
    $\begingroup$ +1 for @kouta-kun's comment. Without focusing on popularity, this is a "why" question, but this answer doesn't explain why one would choose one block-style over another. $\endgroup$
    – BoppreH
    Commented Jul 3, 2023 at 10:26
  • 2
    $\begingroup$ This is a nice survey of all the styles, but doesn't really answer the "why" question. If each style included a pros and cons, it would approach that answer. $\endgroup$
    – Barmar
    Commented Jul 3, 2023 at 15:09
  • 3
    $\begingroup$ The Algol-68 ideas of fi and esac etc seem find as a start, but they are less convenient when spelling procedure or function backwards. $\endgroup$
    – AdrianHHH
    Commented Jul 3, 2023 at 17:26
27
$\begingroup$

Improving readability by reducing word count

(alternatively: it's easier for humans to parse)

Reading source code is hard, and syntax has a big impact on one's ability to skim and search. The most infamous examples are line noise by abusing operators (s()/#?=>$":}\~>\\!;/;, anyone?), but I'd argue that wordy syntax is also bad.

I made a fake piece of code using AEC blocks:

Structure ██████ Consists Of
    ███ ████
    ███ ██████
EndStructure

Function ███(█████, ███, ██) Which Returns ███ Does
    If ████████ Then
        If ███████ █████ Then
             ██████████
             ████████
             Return ██████
        EndIf

        ████████
        █████████
        ██████
    EndIf

    ██████████
    While ████████ Loop
        ████████
        If █████ Then
            █████
        EndIf
    EndWhile
EndFunction

And the same code with C blocks:

struct ██████ {
    ███ ████
    ███ ██████
}

███ ███(█████, ███, ██) {
    if (████████) {
        if (███████ █████) {
             ██████████
             ████████
             return ██████
        }

        ████████
        █████████
        ██████
    }

    ██████████
    while (████████) {
        ████████
        if (█████)
            █████
    }
}

When reading code I can easily ignore all the { } because they are small and redundant with good indentation. But the wordy Consists Of, EndStructure, etc, are too similar to variable names and comments, making it harder to focus on the important bits.

You could run an experiment with two fake languages with either block style. I'd expect that volunteers can search { }-based code quicker than the wordy tokens.


Your point about keyboard layouts is still relevant. I paid extra to have my keyboard in US-layout instead of German, otherwise typing {}[] requires altgr+number keys (?!). If you can think of block delimiters that don't hurt readability and are easier to type, they may make a good alternative, even if they break convention.

$\endgroup$
9
  • 6
    $\begingroup$ I think the ubiquity of syntax highlighting makes a difference to this. It would have been much more significant at the time C was designed, though. $\endgroup$
    – kaya3
    Commented Jul 3, 2023 at 13:10
  • 9
    $\begingroup$ I think this is the real strongest argument against scope-ending keywords. And I say that, even though Basic was my first language too. Actual words, with letters, that I need to read, is too much a price to pay for what is conceptually a big pencil-drawn circle around some code. $\endgroup$ Commented Jul 3, 2023 at 21:05
  • $\begingroup$ At the time C was designed, keywords were standard in mainstream languages such as Fortran, PL/I, and Algol. But also remember that at the time, programs were mostly read on actual paper, printed out on teletype terminals. CRT terminals wouldn't get bold text until the mid 1970s. $\endgroup$
    – Pseudonym
    Commented Jul 4, 2023 at 1:01
  • $\begingroup$ I think it was in TurboPascal that an alternative to "{ block code }" was "(. block code .)". $\endgroup$ Commented Jul 4, 2023 at 11:30
  • 2
    $\begingroup$ @Peter-ReinstateMonica I'd say the point is not so much that keywords and identifiers should be as short as possible. Words contain information that can be useful for readability, though there are limits... Cobol anyone? The actual point is that specifically the EndFoo the information contain only redundant information, and therefore add nothing that would really help readability but only noise. $\endgroup$ Commented Jul 4, 2023 at 20:25
15
$\begingroup$

The reason most languages use } is C did it that way.

$\endgroup$
11
  • 1
    $\begingroup$ But why did C do it that way, and why did so many people consider it such a good idea that they used in their own programming languages? $\endgroup$
    – Philipp
    Commented Jul 3, 2023 at 11:06
  • 11
    $\begingroup$ @Philipp C does it that way because B does it that way :-) $\endgroup$ Commented Jul 3, 2023 at 11:51
  • 1
    $\begingroup$ @Philipp C was born in 1972. I assume that Kernighan & Ritchie would have used the same type of terminal that I used during the early 1970s, an ASR35 teletype. If I have been offered a choice between two languages, which used {...} and begin...end, I know which one I'd have gone for. $\endgroup$ Commented Jul 3, 2023 at 20:17
  • $\begingroup$ @Philipp Why C did it that way is essentially irrelevant. C could have picked anything. The point is that C used it, and C was extremely popular and highly influential (for reasons unrelated to its syntax), so many modern languages are in some way descended from it, and many of them choose to use the same syntax. $\endgroup$
    – Alexis King
    Commented Jul 4, 2023 at 4:22
  • $\begingroup$ @Philipp If you use ink and paper via teletypes (or punch cards, or punched tape) to program, and if you count your mass storage in kB, every character counts. You strip off carriage returns, and you indicate blocks with single characters. $\endgroup$ Commented Jul 4, 2023 at 11:53
11
$\begingroup$

It is not used in most language because indentation makes the extra information in endX statements redundant noise.

In a bit of code that looks like this (please imagine that but ten times as long and twice as nested for the full effect):

if (██████) {
while(██████){
██████
██████
██████
// ██████
██████
}
for(██████; ██████; ██████) {
if (██████) ██████;
██████
██████
}}

Markers to indicate which end or } refers to what would be very useful but programmers don't write code like that. They indent their code to indicate what matches with what that and makes the additional information become redundant. Moreover, (as per @BoppreH's elegant demonstration in another answer) the additional text is more easily confused with the other syntactic markers and so actually makes the code harder to read.

Since indentation, whilst not mandatory in most languages, is expected behaviour of programmers these days - and has been for a long time now - any benefit of having individually tagged end markers is lost.

$\endgroup$
1
  • 2
    $\begingroup$ "Programmers don't write code like that"--unfortunately, some of them do. You should see some of the Java code I have to work with. :P $\endgroup$
    – DLosc
    Commented Jul 5, 2023 at 17:34
5
$\begingroup$

As another answer says, labeling the end constract would be mostly redundant. It would only have semantic value if you allow ending multiple blocks with a single end, e.g.

IF ...
    WHILE ...
        body
ENDIF

In the above, ENDWHILE can be omitted because ENDIF implicitly closes all blocks contained in it.

The only language I can think of where this style is used is HTML. Some tags are automatically closed in some contexts (e.g. you can omit </option> before the next <option>). And HTML parsers will also auto-close some tags even when the language doesn't permit it, to implement the robustness principle.

In a language without distinct end tokens, you can always use a comment to help link the end to the beginning when you write large blocks. But blocks large enough to need this are also generally considered poor coding style. So we don't want to encourage it with language design that makes it easier.

$\endgroup$
3
$\begingroup$

I did want to point out that Turbo Pascal was no innovator when it came to (* and *) substituting for braces. Those digraphs are what I had to use when learning Pascal in 1979 on our Hollerith punched-card mainframe, which used EBCDIC internally, which doesn't even have the brace characters.

Languages that predate the ubiquity of ASCII would certainly be biased towards keywords rather than punctuation for blocking.

Languages developed on slow mechanical I/O devices (punched cards or ASR33 TTY's) would be biased towards shorter rather than longer sequences. They also would not want to care about indention, because re-punching a bunch of cards just because you added an IF in front of them would be a maddening waste of time and materials. Not to mention the risk of running into the maximum width of a punched card.

These early languages would then have set the stage for their descendants.

$\endgroup$
2
  • $\begingroup$ An interesting difference between FORTRAN-era printing devices and those of C's inventors is that the time required to output a line on the former would depend upon the "rarest" character on the chain (many chains would have alphanumerics repeated something like five times, but characters like braces only once), so the chain would have to make at most 1/5 of a revolution to print all of the alphanumerics on a line, but on average half a resolution to output even a single brace character. $\endgroup$
    – supercat
    Commented Sep 10 at 14:40
  • $\begingroup$ The TTY40 we used back in the day had two chains: uppercase-only and mixed case. The UC chain had 5 (?) sets of characters on it, the ULC chain had 3. The former printed listings noticeably faster than the latter, and since listings were all these printers were used for, the fleet was exclusively UC. (I have a ULC chain on my personal TTY40 that I acquired many years ago. I can't remember the last time I fired it up. Laser printers ate chain printers' lunches when they came out.) $\endgroup$
    – jimc
    Commented Oct 2 at 18:03
2
$\begingroup$

The original ALGOL way was that where an explicit END was needed the next token- or at least sometimes the remainder of the card- was treated as a comment.

Hence from Burroughs B5500 ALGOL:

...
    END IF
  END IF % ANYTHING AFTER END IS IGNORED HERE.                                 %
END.     % END MUST BE FOLLOWED IMMEDIATELY BY . HERE ------------------------ %

Derivative languages either dropped provision for an unparsed comment (i.e. any comment had to be marked explicitly), or merged the commonly-used comments into the token (i.e. END IF became ENDIF).

Somewhat later I've realised that I've misread the question. I think OP intended to ask "why do most languages use END rather than ENDIF, ENDWHILE etc." but instead I focused on the ENDsomething aspect.

I'll leave my answer undeleted in the unlikely case that there's something of historical interest in it.

$\endgroup$
2
$\begingroup$

I think your language AEC is really quite interesting.

When it comes to programming languages, there are two main philosophies.

One is drawn from those who prefer natural language (invariably English), the other is drawn from those who prefer symbols. Each has its arguments and its areas of application.

It isn't found strictly necessary to have a special end token in block-structured languages, whether they be English-like or symbol-like. That's essentially how you get as far as Pascal with it's generic begin and end tokens.

In all languages, but especially in the English-style ones, there's a need to balance economy and clarity.

Research shows that people don't read letter-by-letter like computers process strings. When possible, they recognise whole shapes in a gestalt fashion - this applies generally, including applying to words, to sentences, and to blocks.

In English-like languages that have the potential to be almost infinitely verbose, keeping things as short as possible is still quite desirable, to avoid visual clutter and facilitate shape recognition.

So in principle, for those designing a programming language in a rational fashion, end is a perfectly good token. There isn't a need, at first, to justify why it isn't longer, because short is good.

However, experience shows that two situations are not uncommon. The first is to have several levels of blocks nested. The second is to have blocks longer than what can be shown on the screen. And both of these cases occur often enough together even.

Visual Basic (as an example of an English-like language I know well) further helps these situations in two ways. Firstly, it has special keywords to terminate a block which are different depending on the type of block. Secondly, with certain blocks (like For) where the same type of block is commonly nested, you can identify the block by the variable name.

For i = 1 To 5
    For j = 1 To 3
    ...
    Next j
Next i

I'm a regular user of SQL, and it's not uncommon nowadays for single statements (when consistently formatted) to run over several pages on screen, and for blocks to reach several levels deep when writing complicated procedures. Towards the end of a procedure, there is then a series of:

            end;
        end;
    end;
end;

In these cases, I miss VB's special block-ending keywords. And I agree that Pascal (and languages which follow it in this respect, like SQL) is deficient, and that repeating the block type at the end token is found to be a generally useful feature of a language, and it would be more desirable to have a syntax something like so instead:

            end if;
        end while;
    end if;
end procedure;
$\endgroup$
2
  • $\begingroup$ To make up for not having end if and end while, many programmers in C-style languages will put the starting keyword in a comment: end /* if */; $\endgroup$
    – Barmar
    Commented Sep 9 at 20:51
  • $\begingroup$ @Barmar, yes I do so myself! $\endgroup$
    – Steve
    Commented Sep 9 at 21:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .