RR's Ramblings

Consequences of Ada Design Goals
January 4, 2024
by Randy Brukardt

We often are asked why Ada does not have some gadget or other, and the usual reason is that it doesn't meet the design goals. I wrote the following article after one such conversation with the intent of explaining of how the design goals have been applied. I was hoping to make something like this part of the ARG design goals document, but it got pushback about not being "neutral" enough and the thread ran out of steam. I rediscovered it last night in my archives of unfinished projects, and since it is relevant to another comp.lang.ada discussion today, I'm posting it here as personal opionion.

Often, people propose features to make Ada code easier to write. However, "ease-of-writing" (often mischaracterized as "ease-of-use") has never been an Ada Design Goal (see the ARG design goals document). Readability is a design goal, as is simplifying maintenance, always keeping in mind that programmers are human (and thus are limited in how much information they can process and remember, unlike a computer).

Meeting these goals often does enhance ease of writing. For instance, the Ada features of user-defined literals, target name symbols, and user-defined indexing all were added to enhance readability by avoiding duplicative text that provides little information. All of these features also make Ada code easier to write by reducing the amount of text that needs to be written, but that was not the reason that they were added to Ada.

On the other hand, Ada does not have the wide variety of operators found in some other popular languages. This is intentional: humans are bad at remembering what little-used constructs do; maintenance and readability are enhanced by avoiding them. Consider that when a human reading code encounters an unfamiliar operator, they have to go look up what it does. Needing to do so breaks their train of thought and thus slows down their understanding what the code actually does.

Case-in point: the original Ada 2012 proposal was for an "implies" Boolean operator to use in preconditions and other assertions. But the ARG quickly found out that there were many people (including ARG members) who did not intuitively know exactly what function the implies operator implemented. Such an operator would impede understanding of code using it for those people. It was suggested that an if expression would have much the same function (along with others, as it is more general), and every Ada programmer would understand what if x then y else z does. So the if expression was adopted for Ada 2012.

Moreover, to simplify human understanding, the same model was used for case expressions, declare expressions, and quantified expressions. We could have used dedicated syntax and semantics for these constructs, but rather opted for syntax and semantics that are similar to the statements that every Ada programmer already understands.

Similarly, Ada has resisted adding features that would terminate the current iteration of a loop without exiting the loop (the "continue" statement), or to do something after the last iteration of a loop (again, only if the loop is not exited explicitly). Both of these come up occasionally in programming, but were judged not be to common enough to justify a special feature. (Note that both can easily be written using a goto, the first by jumping to the end loop and the second by jumping past the "normal termination" code for an exit.) A rarely used feature could stop a reader cold, requiring them to look up what the feature does before continuing reading. And the goto alternative is neither a lot longer nor harder to understand.

On the other hand, the target name symbol (@) was added to Ada after much discussion, as when used carefully, it can greatly enhance the readability and correctness of Ada code. Consider a line from my web log file analyzer:

   Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
      (Quarter_Item (Month)).Hit_Count :=
   Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
      (Quarter_Item (Month)).Hit_Count + Amount;

This is adding Amount to the Hit_Count component, but you have to careful read both sides of the assignment statement to know that for sure. Contrast the above to the following:

   Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
      (Quarter_Item (Month)).Hit_Count := @ + Amount;

Here it is immediately obvious what the operation is. Moreover, if the target symbol is regularly used, then it is obvious that the following is doing something different as opposed to being a mistake:

   Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
      (Quarter_Item (Month)).Hit_Count :=
   Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
      (Quarter_Item (Month)).View_Count + Amount;

Using the target name symbol also prevents errors by avoiding writing the right-hand side subtly different by mistake.

Examples like these serve to show that while ease-of-writing (often mischaracterized as "ease-of-use") is not a goal of Ada, many proposed features do in fact make Ada easier to write. The ARG is willing to add new features to Ada when they meet the design goals of Ada. But features that are judged to not enhance readability tend to remain omitted from Ada.

The ARG has remained committed to the original Ada design goals as they have made Ada what it is: a language that is usable, understandable, and maintainable. Too much deviation from those goals risk losing what makes Ada what it is.

Copyright © 2024 RR Software, Inc.
Use of this site constitutes your acceptance of these terms of use.