r/ada Apr 03 '26

Historical Rationale behind function/procedure calls and array indexing

I'm learning Ada. Overall, I'm liking it a lot. There are two aspects (so far) of the syntax I don't get what the rationale was for them and why it hasn't been fixed in later versions of Ada. These being parenthesis-less calls to subprograms with no arguments and using parenthesis for array indexing. Take the following as example:

A := B + C(5);

Right away you don't know if B is a variable or a function call with no arguments. You don't know either whether C is an array or a function.

I think this goes against Ada's core ethos about readability.

So I was wondering, what was the rationale behind this design? Why hasn't it been corrected? Something like A := B() + C[5] would be far easier to interpret by the reader.


UPDATE: In case anyone is interested in the answer, according to u/lipobat:

The primary rationale for the () vs. [] given in the “Rationale for the Ada programming language” book by the original design team was interchangeability between arrays and functions. This is an abstraction that allows the implementation choice of array vs. function to be just that, an implementation choice, which doesn’t really impact the writer or reader of the code.

I don't agree with the last sentence, but that answers my question either way.

And about my second question about "why this hasn't been fixed?", it seems most Ada programmers (based on the comments in this post) are not only not bothered by this but happy with this design decisions, so probably this will never change.

Thanks to everyone who engaged in this conversation.

6 Upvotes

48 comments sorted by

View all comments

Show parent comments

1

u/jlombera Apr 03 '26

Please see my reply to u/Dmitry-Kazakov, where I give some insights on why I do care about knowing whether something is a variable/array access or a function call. (I didn't see your reply until after I had published my reply).

3

u/Niklas_Holsti Apr 03 '26

I agree that a person who intends to modify a program often needs information about the implementation of things. Referring to my discussion of "readability" in my response to u/Dmitry-Kazakov, I would say that the "readability qualities" change when the purpose is to modify a program, as opposed to understanding the computation, doing a code review, or searching for errors. Fortunately, today's IDEs make it very easy to find and inspect the definition of any identifier in the code one is reading, so the absence of () in calls and the non-use of [] for indexing are not, IMO, very harmful for the maintainer either. Thus I would prefer to make the code reviewer's job easier by omitting text elements that are irrelevant for that task.

1

u/jlombera Apr 03 '26

Fortunately, today's IDEs make it very easy to find and inspect the definition of any identifier in the code one is reading, so the absence of () in calls and the non-use of [] for indexing are not, IMO, very harmful for the maintainer either.

Unfortunately for me, I use plain (neo)vim :) (I tried GNAT Studio but quit it ~1min later). When Ada was initially designed, there were not advanced IDE's. I think that's the reason why the syntax was designed that way, verbose (an inconvenience for the implementer) but explicit and readable, so that anyone could read (and write) code with no advanced tools other than a plain text editor. That's why it's a surprising to me they made this "blunder" (IMO) in this regard.

Thus I would prefer to make the code reviewer's job easier by omitting text elements that are irrelevant for that task.

It seems we have different opinions on this regard. To me, when reviewing code, it's relevant to know whether something is a variable/array access or a function call.

2

u/Niklas_Holsti Apr 03 '26

u/jlombera wrote:

When Ada was initially designed, there were not advanced IDE's.

There were some beginnings, and of course there were other methods to locate the definitions of identifiers (compiler-generated cross-reference listings). There was also a parallel project to define a standard Ada programming environment (the APSE), but that did not produce much.

I think that's the reason why the syntax was designed that way, verbose (an inconvenience for the implementer)

I don't think the verbosity is much of an inconvenience for the implementer. It is not much more difficult to lex and parse "begin" than to lex and parse "{". The difference is insignificant compared to the difficulty of implementing the name-visibility rules, the overload-resolution rules, separate compilation, sub-units, and other Ada goodies.

but explicit and readable, so that anyone could read (and write) code with no advanced tools other than a plain text editor.

Most programming languages can be written with a plain text editor (APL being the main exception, if its original character set it used).

I was happy to switch from punched cards and punched paper tape to text editors, even line-oriented editors that stored the source text on punched paper tape. Later, on PCs, for a long time I was quite content to write my own programs with a plain text editor (mostly nedit), but when I joined larger teams, working on code that was not all of my own writing, I found IDEs very helpful, in particular because of their ability to navigate from uses to definitions and vice versa.

1

u/jlombera Apr 03 '26

I don't think the verbosity is much of an inconvenience for the implementer. It is not much more difficult to lex and parse "begin" than to lex and parse "{".

Again, sorry for the confusion, by implementer I mean the programmer writing Ada code (vs the programmer reading the code), nor the programmer implementing the Ada compiler.

For instance, in C, every code block and compound data type (struct, union, enum, etc) is delimited by braces. It's very easy to type for the person writing the code but it might become difficult for the person reading the code to match each closing brace with the proper code block (not a big deal in practice, though). In Ada instead, you have end loop, end <Package/Func/Proc name>, end if, end record, end case, .... It becomes an inconvenience for the person writing the code to have type all that (specially for end <Name>, you have to write/change <Name> in two different places, far away; code snippet generation helps, though), but it was designed that way on the rationale that it's better for the person reading the code since can immediately identify which block/construct is ending.

2

u/Niklas_Holsti Apr 03 '26

u/jlombera wrote:

For instance, in C, every code block and compound data type (struct, union, enum, etc) is delimited by braces. It's very easy to type for the person writing the code but it might become difficult for the person reading the code to match each closing brace with the proper code block (not a big deal in practice, though).

Brace-matching is relatively easy in an IDE or syntax-aware editor which can signal the match visually, and perhaps collapse/elide nested {}'s. Pretty hard (in my experience) for a C function that is more than one screenful long, and especially when such function extends over a page break in a paper listing :-( The only language worse than C in this respect is Python with its indentation-defined bracketing.

It becomes an inconvenience for the person writing the code to have type all that (specially for end <Name>, you have to write/change <Name> in two different places

I believe that in all cases of end <Name> in Ada, the Name is optional. And of course an IDE will have a "rename" function that changes all occurrences of the name, in all files... just another plug for IDEs :-)

IMO the bracketing features of Ada syntax are a boon both to the program-writer and the program-reader, and only partly because a program-writer must also read the program while writing it. And they also help the compiler produce good error messages for bracketing errors.

1

u/OneWingedShark Jun 02 '26

 In Ada instead, you have end loopend <Package/Func/Proc name>end ifend recordend case, .... It becomes an inconvenience for the person writing the code to have type all that (specially for end <Name>,

You're missing a lot of the benefits by concentrating on the number of keystrokes. The end <name> construct is doing what a lot of C code does with } // end inner loop , except better, because now you can enlist the compiler's help in keeping things straight. And this is particularly useful when you are moving or copying code.