While I'd been programming in Ada for a few years by then, this was the first time that I truly realized there might be something underlying and guiding the definition of a programming language, that there might be more to creating a programming language than getting a bunch of people together to make up declaration and statement and packaging syntax and semantics.
The realization that a type model for Ada existed turned out to be the genesis of what eventually coalesced into my idea of a programming language's Fundamental Theory. Today I recognize that the Fundamental Theory of Ada is its type model.
By enshrining the type model as its FT, Ada allows a huge amount of information to be intrinsically embedded within Ada programs with a simple type definition. This information is then readily accessible to the developer through the use of Ada's attributes.
Take a simple example:
type Speed_Range is range 0 .. 1000;
With nothing more than a reference to an object of that type:
Speed : Speed_Range;
One can know its minimum value (Speed_Range'First), maximum value (Speed_Range'Last), the minimum number of bits needed to represent all possible values of the type (Speed_Range'Size), the actual number of bits representing a variable of that type (Speed'Size, which is often larger than the type size since objects almost always occupy a whole number of bytes), the number of characters needed to represent the longest possible string representation of values of that type (Speed_Range'Width), etc. You can convert values to and from strings (Speed_Range'Image, Speed_Range'Value), do min/max comparisons (Speed_Range'Min(100, Speed), Speed_Range'Max(Current_Max, Speed)), and use the type as a loop controller ("for S in Speed_Range loop" and "while S in Speed_Range loop"), and more. And none of this information needs to be explicitly programmed by a developer, it is all implicitly provided by the mere definition of the type.
Different kinds of scalar types may have these and other attributes appropriate to the nature of the type: floating point types have attributes for identifying the underlying constituents, accuracy and representational limitations of a floating point type definition, while enumeration types also have attributes to access all positional aspects of the type's values.
Ada's type model extends well beyond simple scalar types: inheritance is obviously based on inheriting from other types via the "tagged" modifier, static polymorphism comes through generic (template) instantiations with the desired types, and dynamic polymorphism is provided by tagged, "abstract" types. Even Ada's concurrency constructs are type-oriented, i.e. task types and protected types (while singletons of each of these can be declared, what goes on under the surface is that a corresponding anonymous type is implicitly defined of which the singleton is the only object). By declaring a subprogram profile type, subprograms themselves are treatable as type objects.
The supporting infrastructure of executable constructs, i.e. the statements, are all then integrated with this type model, and able to exploit the information that is an implicit part of each type, for example in the "loop over the type" statements mentioned above, as well as is done with array handling:
for I in Race_Speeds_List'Range loop ...
There is one area of non-conformance, though. For some reason the whole exception mechanism was not well integrated with the type model. There's nothing that can be construed as an "exception type" in the language (although an exception type mechanism was proposed for the Ada 2005 language revision, it was rejected). So there's no hierarchy of exceptions, no inheritance, and while the Ada 95 revision did incorporate the creation of exception "objects" to which data could be attached (which must, though, be in the form of a string), exception handling is recognized as admittedly inferior to that of most other commonly used languages.
Despite the exception deficiencies, the Ada standard has conformed very well to its fundamental type model theory throughout its evolution. Even when major revisions were made during the Ada 83 to Ada 95 revision process to incorporate full object orientation, that capability was defined as an extension of the existing type mechanisms. (And the fact that improvements to the exception mechanism center around "type-ifying" exceptions show that the type model is recognized as the driving factor of the language's design.)
Understanding and exploiting the Fundamental Theory of Ada leads to more reliable and more efficient software. One issue programmers new to Ada invariably run into and complain about is the preponderance of compilation failures due to "type mismatches" in their code. "Why can't I multiply velocity by a time interval and add it to distance? They're all just numbers!" That's just it, they're not just numbers, they are abstracted entities, each with their own characteristics and constraints, modeled in the language's typing system, and that are only allowed to interact in well-defined ways. Other languages model entities this way, too, except that they require the developer to explicitly model them as classes, while in Ada the language itself effectively performs the type modeling for these kinds of quantities.
By understanding and exploiting the Fundamental Theory of Ada, by understanding what an Ada type represents, an abundance of information about the problem domain can be embedded within the software, can be easily exploited, and can lead to the efficient production of reliable software.