@, Ada 202x, and Janus/Ada
January 4, 2017
by Randy Brukardt
Janus/Ada has used the at-sign '@' as an Ada extension for conditional
compilation since the original version of Janus/Ada in 1981. But the ARG (the
committee in charge of the evolution of Ada) has decided to use @ for a new
Ada feature in the next version of Ada. In order to support that version of
Ada, we'll have to do something with the existing use.
A refresher: The Janus/Ada conditional compilation feature operates lexically.
Specifically, the @ is treated as a space if the /C option is used on the
command line, and as a comment symbol ("--") if the /E option is used on the
command line. (If neither option is used, the compiler will run in no-extensions
mode, and a use of @ will be illegal.)
Since the processing is lexical, any Ada element can be removed from the source
using the @, including pragmas, context clauses (with clauses), and
global declarations, none of which can be removed by the use of a conditional
with a static Boolean value (the traditional Ada way of conditionally compiling
code). Given our initial focus on supporting very small host machines (initially,
CP/M-80 machines with as little as 48K of memory [imagine trying to run anything
in that little memory today!]), we needed to be able to remove anything unneeded
from the production products.
Thus, even today, the Janus/Ada source code heavily uses the @ extension to
mark debugging code. For instance, many Janus/Ada units have lines like:
pragma Debug (Off); pragma RangeCheck (Off); pragma Arithcheck (Off);
@ pragma Debug (On); pragma RangeCheck (On); pragma Arithcheck (On);
which turns on debugging (walkbacks), range checks, and overflow checking only
when conditional compilation is on (/C is used) [note that checking is off by
default]; and
@ with J2Trace, Target_IO;
which includes the Janus2 Trace module and (text) I/O for target types only
if conditional compilation is in use. (It's not usually necessary to allow
customers to do their own compiler tracing.)
We chose @ for this purpose because it is not used for any purpose by the
Ada language, and because it is short and highly visible.
This past summer, the ARG approved a proposal(*) to use @ to represent the
assignment target in the source expression of an assignment. The basic idea
is to use @ to represent the target of an assignment so that it does not
need to be repeated (possibly with a subtle mistake) in the source expression.
(You can see the technical details in
clause 5.2.1 of the draft RM.)
(*) This choice was very controversial. At one point,
I counted well over 30 proposals for this syntax over a 3 month period. Many
other proposals were considered before or since. Most likely, the number of
serious proposals considered is approaching 100. Ultimately, some subset of
ARG members found each other proposal to have a fatal flaw (a different subset
for each proposal). Hardly anyone considered @ their first choice, but no one found a
fatal flaw for it, either. Thus it became the choice.
For example, some of the code in the web log analyzer used on the
www.ada-auth.org site looks like:
if Is_View then
Data.Total.View_Count := Data.Total.View_Count + Amount;
Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
(Quarter_Item (Month)).View_Count :=
Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
(Quarter_Item (Month)).View_Count + Amount;
if not Is_Scanner then
Data.Total.Non_Scanner_View_Count := Data.Total.Non_Scanner_View_Count + Amount;
Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
(Quarter_Item (Month)).Non_Scanner_View_Count :=
Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
(Quarter_Item (Month)).Non_Scanner_View_Count + Amount;
end if;
end if;
The target names are so complex because of the need to allocate memory in chunks,
as the arrays are just too large to usefully keep in memory (at least on a 32-bit machine),
given the large range of months and items [for instance, consider users (that is,
source IPs), most of which only get used a handful of times during a short period of time;
most of the data is empty]. Since they're so complex, it would be very easy to make
a mistake and have the two items subtly different. Obviously, if that happens, the
data gathered would be garbage.
Using the new proposal, @ can be used in place of the complex target name in the
source expression, with the meaning of the target object before it is assigned. The
above code then looks like:
if Is_View then
Data.Total.View_Count := @ + Amount;
Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
(Quarter_Item (Month)).View_Count := @ + Amount;
if not Is_Scanner then
Data.Total.Non_Scanner_View_Count := @ + Amount;
Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
(Quarter_Item (Month)).Non_Scanner_View_Count := @ + Amount;
end if;
end if;
It's obvious that this is easier to read and there is much less chance of
a mistake in this code.
The advantage of using @ this way rather than defining some kludgy new assignment
operation should be obvious: it will work in all contexts. Unlike ":=+" (or maybe ":+"),
@ will work in attributes like Max and in user function calls. For instance, if
we wanted to bound the count above to avoid overflow, we could have used the
Max attribute:
Data.Total.View_Count := Natural'Max(@ + Amount, COUNT_LIMIT);
Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
(Quarter_Item (Month)).View_Count := Natural'Max(@ + Amount, COUNT_LIMIT);
The ARG choose @ for this purpose for a number of reasons. First, @ is short (the
shortest possible), which is important for a shorthand. Second, @ is highly visible
for a single character. One would not want to miss the target name in an expression,
as it obviously changes the meaning a lot. Third, it doesn't already have an Ada
meaning, so it is available without complication (other than for Janus/Ada!). And
it is even mnemonic: what else would one use to represent the Assignment Target (AT)
than the AT sign? (Honestly, that reason was considered more of a joke than a serious
reason.)
The first three reasons are suspiously similar to the reasons that we chose @
to use in Janus/Ada. Obviously, great minds think the same way. (Or maybe they're
just obvious. ☺)
In any case, we'll have to do something with the existing Janus/Ada feature
in order to implement Ada 202x. (The current plan is that 'x' here would be '0',
but of course that could change in the future.) Since the existing Janus/Ada
feature is lexical, it does not seem possible that the features could co-exist.
There seem to be a variety of options:
There probably are other possibilities as well.
We'll have to choose one of these at some point in the future. (Considering
how much shorter some code can be using this feature, and that fact that GNAT
has already implemented it, we'd like to do that sooner rather than later.)
I'd like to hear your thoughts on this choice, especially as we have no way
to know whether you have any code that would be impacted by a change.
Send your thoughts to me at
randy@rrsoftware.com.
|