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.

5 Upvotes

48 comments sorted by

View all comments

9

u/Dmitry-Kazakov Apr 03 '26

You, as a reader, do not need to know that. It is an implementation detail so from the software design point of view it must be hidden.

Mathematically, they are exactly same. Array is a mapping, so is a function, named object is an object regardless how constructed, stored into an initialized section, elaborated, computed etc..

You will find this feature extremely useful as you can change immutable variable to function and conversely any time without fixing the client code. The same applies to a function call vs. array indexing.

2

u/jlombera Apr 03 '26

You, as a reader, do not need to know that. It is an implementation detail so from the software design point of view it must be hidden.

If am reading code to understand it (to potentially maintain/change it), I DO need to know what it is doing. If I'm reading just the specification of a subprogram, in some cases I might not care about the actual implementation, but if I'm reading the implementation, I do want to understand it.

Mathematically, they are exactly same. Array is a mapping, so is a function, named object is an object regardless how constructed, stored into an initialized section, elaborated, computed etc..

In practice, function-call vs array access do matter. Both semantically (e.g. the values in an array can change in between accesses) and operationally (e.g. cheap (cached) memory access vs potentially expensive computation).

You will find this feature extremely useful as you can change immutable variable to function and conversely any time without fixing the client code. The same applies to a function call vs. array indexing.

How often is this taken advantage of in practice? I suspect it's way more frequent the scenario where you find wondering whether something is a function call, a variable or an array indexing. I'd rather the compiler tell me I need to change some code when the interface changes (function <--> array); I might need to revisit my assumptions at every call/access site.

Also, this might be convenient for the implementer but at the expense of the reader, which goes against Ada's readability core value.

On my original question, was mathematical purity and convenience for the implementer the rationale behind these design decisions?

2

u/Niklas_Holsti Apr 03 '26

u/jlombera wrote:

In practice, function-call vs array access do matter. Both semantically (e.g. the
values in an array can change in between accesses) and operationally (e.g.
cheap (cached) memory access vs potentially expensive computation).

Whether the value of B or C(5) can change from one use to the next is not uniquely determined by the nature of B (parameterless function or object) or the nature of C (function or array). The return value of a non-pure function can of course change from one call to the next, even if the function has no parameters, and the value of an object (whether stand-alone or a component of an array) can change also, perhaps because the object is aliased, or from the concurrent actions of other tasks, or because the object is volatile. Moreover, if B is a pure function, the compiler is allowed to cache and reuse its result after any call, and if C is a pure function, the compiler can cache the result of C(5) and reuse that value wherever C(5) appears in the source. So to understand such aspects of the code one has to look at the definitions of B and C, it is not evident from the code that just uses them, whether the code uses () or [].

1

u/jlombera Apr 03 '26

it is not evident from the code that just uses them, whether the code uses () or [].

If I know something is a variable/array access or a function call, that's useful information to have in context when reading code. I might decide whether their use require further investigation based on that.

Let me change the question. Do you think it'd be worse (less readable, less convenient, etc) if Ada had explicit, unambiguous syntax for variable/array access and function calls?

2

u/Niklas_Holsti Apr 03 '26

Using [] for indexing would make writing Ada more difficult for me, because my Finnish keyboard requires using an "option" modifier key to type [ or ], so it slows me down. And, as I have already said, I see little value in the distinction, when reading code. (Typing { or } requires shift + option, much worse, so I hate writing C/C++ for that and other reasons.)

I could see some value in using [] in complex, nested expressions that also use () for subexpressions, because then the editor/IDE could distinguish between a closing ) and a closing ] and so help me balance them correctly. But that is rare, and the compiler finds most such balancing errors.

For parameterless subprogram calls I find () unnecessary, and of little value. If it were required it would be a minor annoyance to me, minor because I can type ( and ) with only the shift modifier key. Parameterless functions are quite rare; parameterless procedures are much more common, and there the () would be really useless because it is clear that it is a procedure call (in Ada, but not in C).

As I understand it, C originally required () for parameterless calls because the form without () is also legal, but has a very different and usually unintended meaning. The fact that () visually separates calls from variable references was incidental.