TL;DR:
It carries no special advantage, but it is a necessary part of any language that allows block declarations.
Ada's method of block termination is very explicit, but that does not convey any particular advantage.
Discussion
As the others are indicating, it is simply Ada's method of block termination. The syntactic detail is not very important, but the semantics are critical.
Consider a few examples of how block termination is expressed in other languages:
- Erlang and Prolog: The keyword
end
for lambdas and conditionals, the full-stop character .
for function definitions, and ,
and ;
for phrase segregation.
- Python, YAML, "indented-mode" Haskell and RyuQ: Block termination is expressed with indentation (semantic whitespace).
- Ruby and Elixir: Block termination is expressed with the keyword
end
. Very similar to Ada.
- Algol and sh/Bash/Dash: Express block termination with a matching reversal of the initiating keyword. So
if
is terminated with fi
, and case
terminates with esac
.
- Rust, C, C++, D, Java, JavaScript/ECMA Script, etc.: All open using a keyword and an opening bracket
{
and close all blocks with a closing }
which allows the block's closing token to be easily identified (by a relatively naive parser, not necessarily a human reader).
- Lisp and friends: Generally speaking, everything is a list-form semantic expression and all evaluation blocks are opened with a
(
(which essentially means "begin evaluation") while lists are opened with a '(
(which essentially means "I'm a list, but skip evaluating me") and all are closed with a closing paren )
.
- XML, HTML, SGML, XSLT, and other waking nightmares: Each (possibly arbitrary) opening tag like
<foo>
is terminated by a matching tag with a closing slash indicator like </foo>
(or sometimes just </>
, which is slightly better for important reasons). Most variants of angle-bracket madness now support "self-closing" tags as well, where the closing slash can exist at the end of the opening tag as a shorthand, so an empty tag that is its own phrase like <foo></foo>
can be shortened to <foo />
or sometimes <foo/>
depending on what flavor of insanity it happens to be. (Obviously there are a few semantic black holes one can be sucked into with this sort of ambiguity.)
Etc...
As we can see, Ada's block termination expression isn't in any way odd -- it is just more explicit than the brackety languages (and slightly more explicit than Erlang and Prolog). Regardless, without resorting to GOTOs or other forms of explicit jump expression it is absolutely necessary to be able to define logical blocks and that means also having a clear way of terminating them. This is just Ada's way.
Addendum: A note on "blocks"
As flyx has kindly pointed out, there is an interesting distinction among statements, blocks, expressions, procedures, subroutines, and functions. Some languages have all of these, some have only functions and expressions, some have any arbitrary mix.
C (and its descendants) make a distinction between "statements" and "expressions", and in the case of a C-style block delimited by {}
braces the language treats that "block" as a single statement. Meaning, it is not expected to return a value the way an expression would, it is expected that execution of the procedure represented by the statement itself will have some meaningful effect on the program.
So what does that mean in terms of syntax?
In Ada the if
explicitly opens a block.
In C, however, an if
doesn't open or declare anything at all -- it simply guards the next statement. So a one-line if
in C is perfectly normal to see -- no brackets need be closed at all because none were opened. If, however, the statement following the if
needs to be a multi-line statement (note that it is a bit unidiomatic in C to define functions for every multi-line statement).
if (x) {do_something();}
is the same as
if (x) do_something();
Look, Ma, no brackets!
The former is more common as most conditional branches are more than a single line.
This particular issue becomes a bit more cloudy (and now you actually do get into branch prediction issues) with the C ternary operator:
int opening_time = (day == SUNDAY) ? 12 : 9;
or Python's somewhat recently added ternary operator:
opening_time = 12 if day == 'sunday' else 9
An equivalent (in somewhat unidiomatic C):
int opening_time = 9;
if (day == SUNDAY) opening_time = 12;
And so on.
Here is where we get into understanding both some of the very early utility of un-named blocks of code, and how in some (but not all) languages they are treated as compound statements.
if
blocks, use characters (such as{
and}
) to delimit blocks, or have something equivalent toend if
. – Poignant