More about types
Aggregates: A primer
So far, we have talked about aggregates quite a bit and have seen a number of examples. Now we will revisit this feature in some more detail.
An Ada aggregate is, in effect, a literal value for a composite type. It's a very powerful notation that helps you to avoid writing procedural code for the initialization of your data structures in many cases.
A basic rule when writing aggregates is that every component of the array or record has to be specified, even components that have a default value.
This means that the following code is incorrect:
There are a few shortcuts that you can use to make the notation more convenient:
To specify the default value for a component, you can use the
You can use the
|symbol to give several components the same value.
You can use the
otherschoice to refer to every component that has not yet been specified, provided all those fields have the same type.
You can use the range notation
..to refer to specify a contiguous sequence of indices in an array.
However, note that as soon as you used a named association, all subsequent components likewise need to be specified with named associations.
Overloading and qualified expressions
Ada has a general concept of name overloading, which we saw earlier in the section on enumeration types.
Let's take a simple example: it is possible in Ada to have functions that have the same name, but different types for their parameters.
This is a common concept in programming languages, called overloading, or name overloading.
One of the novel aspects of Ada's overloading facility is the ability to resolve overloading based on the return type of a function.
Note that overload resolution based on the type is allowed for both functions and enumeration literals in Ada - which is why you can have multiple enumeration literals with the same name. Semantically, an enumeration literal is treated like a function that has no parameters.
However, sometimes an ambiguity makes it impossible to resolve which declaration of an overloaded name a given occurrence of the name refers to. This is where a qualified expression becomes useful.
Syntactically the target of a qualified expression can be either any expression in parentheses, or an aggregate:
This illustrates that qualified expressions are a convenient (and sometimes necessary) way for the programmer to make the type of an expression explicit, for the compiler of course, but also for other programmers.
While they look and feel similar, type conversions and qualified expressions are not the same.
A qualified expression specifies the exact type that the target expression will be resolved to, whereas a type conversion will try to convert the target and issue a run-time error if the target value cannot be so converted.
Note that you can use a qualified expression to convert from one subtype to another, with an exception raised if a constraint is violated.
X : Integer := Natural'(1);
As noted earlier, each enumeration type is distinct and incompatible with every other enumeration type. However, what we did not mention previously is that character literals are permitted as enumeration literals. This means that in addition to the language's strongly typed character types, user-defined character types are also permitted: