Ada Watch: Getting the right programming language for the job

Ada is a strongly typed language that is a natural choice for developing high-reliability programs. Some languages such as C are good at low-level-programming but not for solving other challenges – as covered in my previous blog. You need to choose the right tool for the job. Instead of using one language or one tool for every problem, you should provide engineers with multiple options for developing high-reliability software – which is where Ada shines.

(To read Greg’s previous blog, click here.)

We can illustrate this via a high level perspective, without diving into specific language details and individual preferences. The first step is to look at how a strongly typed language can avoid hard-to-detect errors and incorrect program operation.

Advantages of Ada for high-reliability applications

Many of Ada’s high-reliability software development features are not unique to Ada and may be found in a variety of other (strongly typed) programming languages. What makes Ada unique is that all of these features are found in a single programming language.

Ada is strongly typed. In a simple sense this means that variables, constants or objects must be declared before they are used. However, it also allows the language (compiler) to statically check the validity of the use of such identifiers.

An interesting effect of this design feature is that typos and misspellings can be detected as mistakes and flagged for the developer. Any human writing endeavor will have errors and as the size of the total writing effort increases so will the number of mistakes. Since many misspellings look correct to the casual observer (or they wouldn’t have happened in the first place) having the language detect these is extremely valuable.

Let’s say the programmer intends to calculate a vehicle Location “L” as a real number and is using an integer Index “I” for another purpose. In Ada, if the variable “I” is used instead of “L” it will be flagged as an incorrect type for the operation. In C, the Index “I” will be implicitly converted (“cast”) to a real value in the expression, introducing a bug. Simple mistakes like this can remain hidden, be difficult to discover, and have insidious changes in the expected meaning of the software. For example, “Location” was expected to be the vehicle location at this point in the program, but Index “I” is something unrelated.

Ada supports the concept of separation of program specification from implementation. The language supports the definition of a program specification that is visible and callable by outside program units. This defines the name of the unit or subprogram as well as its parameters, their types, etc. The specification is made visible to callers using the “with” construct. The implementation, or body, may be in a separate file and contain the full algorithmic implementation of the specification. This unit is always semantically invisible to callers. Thus internal details of the algorithm cannot be altered by a caller.

This is a critical feature of the Ada language in that collections of units may be developed, tested and verified individually without worry of side effects occurring when they are combined. This allows for the building of layered or component architectures in a safe and scalable way. Ada has additional functionality in this area, but this basic concept is critical for developing high-reliability applications. Other languages with a “#Include” style feature semantically “include” the source text of the referenced unit into the source of the referencing/calling unit. This makes all internal details visible to the caller and allows for accidental or purposeful side-effects to take place. In small programs it may be possible to manually detect manipulation of these internal details. The reader can see that a typo in the caller may accidentally reference and change an internal value within the called unit (the developer typed an “X” that is not declared locally, but ends up referencing “X” in the unit being included). Once again, in larger programs simple references like this can remain hidden, be difficult to discover and have insidious changes in the expected meaning of the software.

By combining these two simple features, the language can also check to see if two identifiers can be used together in an expression. Assuming two variables “A” and B” declared as:

• A and B are of Integer types
• A is an Integer and B is of a Color type

Plus (“+”) is a subprogram with a defined number of parameter inputs, of a certain type, with a specific return value type. Is there an operation “+” for 1 “+” 1 (A+B)? Is there an operation “+” for 1 “+” BLUE (A+B)? In the case of the C language, both of these expressions are likely to be valid, but what do they mean? In the case of Ada, the first expression is valid but the second is not. Again, such errors are caught early in the development life cycle where they are easily and cheaply found and corrected.

I’ve outlined just a few of the high-level advantages of some very basic features of Ada so they may be understood at an intuitive level as to why these make programs easier to develop without errors. The Ada language has many more features that can aid in developing high-reliability programs, and I encourage you to explore and test them for yourselves.