Please send all questions & assignments to:
dsolarek@eng.utoledo.edu

 

Ada
Introduction and Overview

Ada is a computer programming language originally designed to support the construction of long-lived, highly reliable software systems. Ada is based on Pascal, but extends and modifies it in ways which emphasize readability, avoids error-prone notation, encourages reuse and team coordination, and supports efficient implementation.

A significant advantage of Ada is its reduction of debugging time. Ada tries to catch as many errors as reasonably possible, as early as possible. Many errors are caught at compile-time by Ada that are not caught or are caught much later by other computer languages. Ada programs also catch many errors at run-time if they cannot be caught at compile-time (in most implementations, this checking can be turned off to improve performance). In addition, Ada includes a problem (exception) handling mechanism so that these problems can be dealt with at run-time.

Learning Objectives

The purpose of this lesson is to introduce you to the basic structure and syntax of Ada programs. At the end of this lesson, you should be able to:
  • describe the proper syntax for Ada declarations, statements, variable names, expressions, and comments
  • create simple Ada programs
  • compile Ada programs using the GNU gnatmake utility and gcc compiler
  • debug common errors in Ada programs

History and Language Standards

Ada is a high-level programming language developed in the late 1970s and early 1980s for the United States Department of Defense (DoD) for real-time embedded systems. Ada is the most commonly used language in U.S. weapons systems modernization. Ada was designed to be a general-purpose language for everything from business applications to rocket guidance systems. One of its principal features is that it supports real-time applications. In addition, Ada incorporates modular techniques that make it easier to build and maintain large systems. Since 1986, Ada has been the mandatory development language for most U.S. military applications. In addition, Ada is often the language of choice for large systems that require real-time processing, such as banking and air traffic control systems.

Ada is named after Augusta Ada Byron (1815-52), daughter of Lord Byron, and Countess of Lovelace. She helped Charles Babbage develop programs for the analytic engine, the first mechanical computer. She is considered by many to be the world's first programmer.

Ada was originally standardized by ANSI in 1983 (ISO released an equivalent standard in 1987). Ada was recently revised to add some new capabilities; this revision is called "Ada 9X" or "Ada 95." Ada compiler vendors have generally updated their compilers to add the new Ada 95 features. This lesson covers the capabilities of Ada 95, but will note which features are new to Ada 95.

Ada is officially defined in its reference manual (RM). The complete Ada 95 RM is available on-line as a hypertext document. However, the RM is not intended to be a tutorial and can be hard to understand if you're not already somewhat familiar with Ada.

Ada was not designed by a committee. The original Ada design was the winner of a language design competition; the winning team was headed by Jean Ichbiah (Ichbiah's language was called "Green" after the designation of his design team). The 1995 revision of Ada (Ada 95) was developed by a small team led by Tucker Taft. In both cases, the design underwent a public comment period where the designers responded to public questions and suggestions.

Ada 95 is the first internationally standardized OO language (ISO, ANSI, FIPS) as it supports the essential features of object orientation ... including full inheritance and run-time polymorphism in addition to the abstraction and encapsulation always supported.

Current Standards

Following the success of the original release of the Ada language in 1983, a standards committee was formed to review the language and update it if necessary. Since the time of the original release of Ada, there has been a large increase in the knowledge of better ways to structure code for both efficiency and correctness, so changes were made to the Ada language to incorporate some of this additional knowledge. The new standard was completed and approved in 1995, and it has been called Ada 95. The original language is often referred to as Ada 83, but either can appropriately be called Ada.

Why Study Ada?

Ada was developed to be more than just another programming language, since it embodies many of the modern principles of software engineering. For example, the concept of information hiding is designed into the language, rather than being implemented as a programming technique. Ada was also designed for developing large software systems, and is therefore applicable for use in programs containing hundreds of thousands, or even millions of lines of source code. It is obvious that a single person could not write such a large program in a reasonably short time, a team effort being required to complete it. Ada is especially well suited for use in a team environment.

Ada Basics

The following examples illustrates the basic elements of a simple Ada program.
 -- Print a simple message to demonstrate 
 -- a trivial Ada program.
 with Ada.Text_IO;
 procedure Hello is
 begin
  Ada.Text_IO.Put_Line("Hello, world!");
 end Hello;
The primary aspects of this example include statement termination, case sensitivity, use of whitespace (e.g., spaces), comments, and compound statements (or blocks).

Termination. The semicolon is a statement terminator in Ada. It tells the compiler that this particular statement is complete at this point. The semicolon at the end of the procedure is also a statement terminator since a procedure, and hence the entire program, is defined as a complete statement in Ada.

Case. Ada allows you to use either case for alphabetic characters in an identifier and you can freely mix them up in any way you desire. Good programming practice, however, would lead you to select a convention for where to use upper case and where to use lower case. A good selection of case could be an aid to understanding the program since it would convey some information about what the identifiers.

Ada Comments. Comments convey no information to the computer, they only aid the writer and reader of the program to understand what the original writer was trying to do within the program. All comments in Ada begin with a double minus sign, or double dash if you prefer, and continue to the end of that line. No spaces are allowed between the dashes, they must be adjacent. There is no provision for middle-of-line comments in the Ada language, only end-of-line, although they can be an entire line as illustrated in the first two lines of the example program.

Comments can be placed nearly anywhere in an Ada program, including prior to the program, and following the end of it. The example programs shown on this page provide some illustrations of where comments can pe placed.

White Space. Ada programming is free form, allowing you to add spaces and blank lines anywhere you wish to make the program clear and easy to understand, provided of course that you do not split up an identifier.

Ada Statements. Statements are the means by which Ada allows the programmer to manipulate data. Statements are executed. Conventional Ada statements include:
  • assignment
  • exit
  • goto
  • if
  • loop
  • case
  • block
Statements related to subprograms are:
  • procedure call
  • return
Statements which are related to the more advanced concepts of the Ada language include the following:
  • delay
  • raise
  • entry call
  • abort
  • code
  • accept
  • select
Any statement may have a label which serves as a symbolic address for the statement.

Compound Statements. Ada allows you to define the equivalent of a procedure and execute it as inline code. Such a section of code is called a block and is constructed by using three reserved words, declare, begin, and end, with declarations between the declare and begin, and executable statements between the begin and end. Any new types, subtypes, variables, constants, and even subprograms can be declared in the declaration part of the block and used in the executable part. The scope of the declarations begin where they are declared, and end at the end of the block.

A block is a single statement and because it is, it can be put anywhere that it is legal to put any other executable statement. It could be used within a loop, in one branch of an if statement, or even as one of the cases of a case statement.

If no declarations are needed, you can declare a block without the reserved word declare, using only the execution block delimiters begin and end.

Here is another version of the "Hello World" program.

 with Ada.Text_IO;
 use Ada.Text_IO;
 procedure Hello1 is
   -- no variables needed here
 begin
   Put ("Hello world from Ada!");
 end Hello1;
Can you identify what is different from the previous one? More about this program later.

Program Organization

The basic syntax for an Ada program is illustrated below:
 procedure <Program_Name> is
    <Declaration_Part>
 begin 
    <Executable_Part>
 end <optional repeat of Program_Name>;
There are two portions of any Ada program, a Declaration_Part, which is contained between the reserved words is and begin, and the Executable_Part which is contained between the reserved words begin and end.

The Program_Name must match the file name for your Ada program. For example, if your Program_Name is HelloWorld, the file name must be HelloWorld.adb. For the class server and the GNAT compiler that we are using, there is no case sensativity, e.g., HelloWorld in the program file is considered to be the same as helloworld.adb in the file name.

Following the reserved word end is a repeat of the Program_Name and a semicolon. Repeating the Program_Name is optional but, as a matter of style, you should get into the habit of including it at the end of every program. The semicolon is required to end the program.

An Ada program is composed of one or more program units. A program unit can be a:

subprogram
which define executable algorithms. Procedures and functions are both subprograms.
package
which defines a collection of entities. Packages are the main grouping mechanism in Ada, somewhat analogous to Modula's "module."
task unit
which defines a computation that can occur in parallel with other computations.
protected unit
which can coordinate data sharing between parallel computation. This did not exist in Ada 83.
generic units
which helps to make reusable components (C++'s templates are similar).
The latter three are advanced topics, so we will concentrate for now on packages and subprograms.

The package is structurally the most important kind of program unit. Most Ada programs are basically a set of a large number of packages, with one procedure used as the "main" procedure to start the Ada program. Packages can represent an abstract data type or a set of data objects shared among subprograms.

Including the notion of packages, a more accurate specification of a basic Ada program would include the package statements at the top of the program file. This is illustrated below:

 with <Package1_Name>; use <Package1_Name>;
 with <Package2_Name>; use <Package2_Name>;
     .
     .
     .
 with <PackageN_Name>; use <PackageN_Name>;
 procedure <Program_Name> is
   Variable1 : <Some_Type>;
   Variable2 : <Some_Type>;
     .
     .
     .
   VariableM : <Some_Type>;
 begin
   <Statement_1>;
   <Statement_2>;
     .
     .
     .
   <Statement_K>;
 end <Program_Name>;
Some programs might be very wordy if you had to always specify where a procedure is defined in order to use it. Thus, Ada provides the use clause. Whenever you use a procedure (or something else) but do not specify where it is defined, the Ada compiler will search all units listed in applicable use clauses. Use clauses follow the with clause, begin with the keyword use, and then list the library units to be searched. Here is how that first program would look with a use clause:
 -- Print a simple message to demonstrate a 
 -- trivial Ada program.
 with Ada.Text_IO;
 -- use clause - automatically search Ada.Text_IO.
 use Ada.Text_IO;
 procedure Hello2 is
 begin
  -- Note: No longer has "Ada.Text_IO" in front.
  Put_Line("Hello, world!");
 end Hello2;
The following program uses the previous "Hello world!" program as a package.
 with Hello;
 -- refer to compiled version of Hello
 procedure Hello3 is
 begin
   Hello;
   Hello;
   Hello;
 end Hello3;
Here is another example Ada program, one which includes a Declaration_Part for a string constant.
 -- filename UltimateAnswer.adb
 -- Introductory Ada 95 program 
 -- for Chapter 12, Section 12.1.
 -- October 22, 1999
    
 with Ada.Text_IO;
 use Ada.Text_IO;
        
 procedure UltimateAnswer is
    
   The_Answer : constant STRING := "forty two.";
    
 begin -- UltimateAnswer 
   Put_Line( "The answer to life, the universe");
   Put_Line( "and everything else is - ");
   Put_Line( The_Answer );
 end UltimateAnswer;
Compare this program to the previous examples.

Now lets look at an example which illustrates how to get input from the user.

 with Ada.Text_IO; use Ada.Text_IO;
 procedure Get_Name is
   Name   : String (1..80);
   Length : Integer;
 begin
   Put("Enter your first name> ");
   Get_Line(Name, Length);
   New_Line;
   Put("Hello ");
   Put(Name (1..Length));
   Put(", we hope that you enjoy learning Ada!");
 end Get_Name;
Note that New_Line is used in the program above to enter a "carriage return, line feed" into the output stream.

Variables and Identifiers

An identifier is a name we use to refer to any object in Ada and it must be formed by following some fairly rigid rules. We will list the rules for forming a valid identifier, then make up a few for illustrative purposes.
  1. An identifier must start with a letter of the alphabet.
  2. Following the initial letter, the identifier can be made up of as many letters, numbers, and underscores as desired, provided that the underscores occur only singly (not two in a row), and an underscore is not the last character.
  3. Case of letters is not significant.
  4. There is no limit to the length of an identifier but each identifier must fit on one line of text and the writer of the compiler may impose a line length limit. The minimum line length must be at least 200 characters.
  5. No blanks or special characters can be used as part of an identifier.
With these rules in mind, lets make up a few valid identifiers:
 Ada           -- A perfectly valid identifier
 ADA           -- The same one, case doesn't matter
 Ada_Compiler  -- A very descriptive identifier
 The_Year_1776 -- Another descriptive identifier
 a1b2c3d4e5f6  -- Very nondescript, but valid
and a few invalid identifiers:
 12_times_each  -- Can't start with a number
 This__is__neat -- Multiple underlines illegal
 This is neat   -- blanks illegal
 Ada_"tutorial" -- special characters illegal
By this time you should be able to recognize a valid Ada identifier. It may seem like a lot of effort to define a valid identifier in such detail, However, you will be naming everything you use in Ada, so you must know how to name things before you can do anything meaningful with the language.

In addition to an identifier being correct, it should also be easy to use and meaningful. For example, consider the following list of valid Ada identifiers and see which convey to you some idea of what they represent.

  GrossPay
  Time_Of_Day
  dateofbirth
  Final_Score
  Get_the_Current_Temperature
  X24
  atx
  w
  t2
Ada was designed to be written once and read many times. This is truly what happens with any non-trivial program designed and developed by a group of persons. As such, little attention is paid to the fact that it may be a bit tedious to key in long identifiers when the program is being written. The extra effort pays off when it is read repeatedly, since it is so easy to follow the logic of the program. The first three identifiers above are preferred because of the information they convey to the reader, and the last three are to be considered of little value in defining the program logic. Of course, if you were using a mathematical relationship that used the variable named "t" in its calculations, that particular name for a variable might be a good choice.
 -- Demonstrate a trivial procedure, with 
 -- another nested inside.
 with Ada.Text_IO, Ada.Integer_Text_IO;
 use Ada.Text_IO, Ada.Integer_Text_IO;
 
 procedure Compute is

  procedure Double(Item : in out Integer) is
  begin -- procedure Double.
    Item := Item * 2;
  end Double;

  -- Local variable X of type Integer.
  X : Integer := 1;

 begin -- procedure Compute
  loop
   Put(X);
   New_Line;
   Double(X);
  end loop;
 end Compute;
Note that the local variable called X is of type Integer with an initial value of one. Integers are used when you want to store possibly signed integers, and you don't care what the minimum and maximum range is. As we will see later, there are other things you should do if you do care what the minimum and/or maximum range is.

Inside this new procedure is a local procedure called "Double," which takes a value and doubles it. A local procedure, like a local variable, can only be used inside the procedure surrounding it. This capability to nest procedures inside other procedures is useful in larger programs and is a standard capability in Pascal (though not in C or C++).

The phrase in out means that the value is both received and changed in the procedure.

The phrase with .. Ada.Integer_Text_IO permits use of a predefined Ada 95 package for performing text input and output on Integers. It includes an operation named Put that will print an Integer sent to it.

The second begin statement defines the Compute procedure itself. Compute has an infinite loop, which prints the current value and doubles it repeatedly. Put prints out the number, and New_Line causes the text to go to the next line.

Computers cannot really compute an infinitely large value; sooner or later they will run out of space to store the number. What will happen in this case? Some programming languages (notably C) simply permit garbage to be computed. Ada has a better approach: when a computation (such as doubling) cannot be performed, Ada raises an "exception." Thus, sooner or later this program will halt with a message explaining why and where it halted. As we will learn later, these exceptions can be caught and handled inside the program.

Keywords

Ada 95 uses 69 identifiers which are called reserved words or keywords. Note that Ada 83, by contrast, only had 63 keywords. Keywords are reserved for specific uses within an Ada program and cannot be used for any other purpose. As you study the language, you will see very clearly how to use each of the keywords and why these particular words were chosen. Since Ada is a large language containing many options and cross checks, writing an Ada compiler is an enormous job, but the use of keywords simplifies that job. The keywords also make the final program much easier to read and understand.

The 69 Ada 95 keywords include the following:

 abstract  abort    accept    access   aliased
 all       and      array     at       begin
 body      case     constant  declare  delay
 delta     digits   do        else     elsif
 end       entry    exception exit     for
 function  generic  goto      if       in
 is        limited  loop      mod      new
 not       null     of        or       others
 out       package  pragma    private  procedure
 protected raise    range     record   rem
 renames   requeue  return    reverse  select
 separate  subtype  tagged    task     terminate
 then      type     until     use      when
 while     with     xor
Remember, Ada keywords may not be used as identifiers.

Declarations

Program units (including subprograms and packages) normally consist of two parts:
  1. The declaration, which contains information that will be visible to other program units. The declaration defines the interface for a program unit. Sometimes people refer to a declaration as a specification. They are somewhat analogous to the contents of C/C++ header "*.h" files.
  2. The body, which contains implementation details that need not be visible to other parts. They are somewhat analogous to the contents of C/C++ implementation "*.c" files.

These separate parts of program units are usually stored in separate files. This explicit distinction between declaration and body allows a program to be designed, written, and tested as a set of largely independent software components.

There are two special cases to help make programming easier:

  1. Separate declarations are not required for subprograms (procedures and functions). If a subprogram has a body but no declaration, the body of a subprogram can serve as its own declaration. This makes writing the `hello, world' program in lesson 1 easier - technically, that simple program is a procedure body that automatically gives its own declaration.
  2. For some packages, it's not possible to have implementation details. For example, a package declaration could be just a collection of constants (like pi and the square root of 2). In this case, the package must not have a body, since one isn't needed. This is relatively rare - most packages need both a declaration and a body.
Ada requires that anything you use must have been previously defined. This includes variables as well as constants, procedures, functions, and all other entities. At least one language, Pascal, makes this claim but breaks it in a few instances. Ada never breaks this rule.

Data Types

Every variable (object) has an associated type. The type defines a set of values which the variable can have assigned to it, and it also defines a set of operations that can be performed on the variable. Ada is a strongly typed language since it has very strict rules limiting how a variable can be used. The compiler will not give you a usable program unless you follow all of the rules very carefully. A major part of the study of Ada involves the study of types and how to use typing as a programming aid.

Ada is a block-structured language in which the scope of declarations, including object and type declarations, is static. Static scoping means that the visibility of names does not depend on the input data when the program is run, but only on the textual structure of the program. Static properties such as visibility can be changed only by modifying and recompiling the source code.

Types in Ada can be categorized in a number of different ways. There are elementary types, which cannot be decomposed further, and composite types which, as the term implies, are composed of a number of components. The most important form of composite type is the record which comprises a number of named components themselves of arbitrary and possibly different types.

A broad hierarchical classification of Ada data types is illustrated below:


                      Ada Types
                          |
            +-------------+-----------+
            |                         |
     Elementary Types           Composite Types
            |                         |
     +------+----+       +-------+--------+------+
     |           |       |       |        |      |
   access      scalar  array  record  protected task
                 | 
         +-------+--------------+
         |                      |
     discrete                  real
         |                      |
    +----+-------+         +----+-------+
    |            |         |            |
enumeration   integer     float        fixed
                 |                      |
           +-----+-------+        +-----+-----+
           |             |        |           |
         signed       modular   decimal    ordinary
Types. A type is either an elementary type or a composite type. Elementary types cannot be decomposed further whereas the composite types have an inner structure. The elementary types can be further categorized into the scalar types and access types. The composite types comprise familiar array and record types plus the protected and task types which are concerned with multitasking.

Scalar Type. Scalar types are themselves subdivided into the discrete types and the real types. The discrete types have certain important common properties; for example, they may be used to index array types. The discrete types are the enumeration types and the integer types. The integer types in turn comprise signed integer types and modular (unsigned) types. The real types comprise the other forms of numeric types.

Enumeration Type. An enumeration type defines an ordered set of distinct enumeration literals, for example a list of states or an alphabet of characters. The enumeration types Boolean, Character (the 8-bit ISO standard character set) and Wide_Character (the 16-bit ISO standard character set) are predefined.

Numeric Type. The numeric types do not exactly fit the hierarchy as presented, but there are certain properties that are common to all numeric types (such as the availability of arithmetic operations), so we have indicated this by a double box surrounding the numeric types. Numeric types provide a means of performing approximate or exact numerical computations. Approximate computations may be performed using either fixed point types with absolute error bounds, or floating point types with relative error bounds. Exact computations may be performed with either integer types, which denote sets of consecutive integers, or with decimal fixed point types. The numeric types Integer, Float and Duration are predefined.

 with Ada.Float_Text_IO;
 use Ada.Float_Text_IO;
 procedure Think is
   -- A and B initially zero,
   -- note the decimal point
   A, B : Float := 0.0;
   I, J : Integer := 1;
 begin
   A := B + 7.0;
   I := J * 3;
   B := Float(I) + A;
   Put(B);
 end Think;
Access Type. Access types, the remaining form of elementary type, allow the construction of linked data structures. The value of an access type is, in essence, a pointer to an object of another type, the accessed type. The accessed type may be any type. In particular the accessed type may be a class-wide type thereby allowing the construction of heterogeneous linked data structures. Access types may be used to designate objects created by allocators, declared objects (provided they are marked as aliased) and subprograms.

Array Type. Array types allow definitions of composite objects with indexable components all of the same subtype. Array types may be of one or more dimensions. The types of the indexes must be discrete. The array types String and Wide_String are predefined.

Record Type. Record types are composite types with named components not necessarily of the same type. Record types may be tagged or untagged. A tagged record type may be extended upon derivation and gives rise to a class-wide type which forms the basis for dynamic polymorphism. A tagged record type may also be marked abstract in which case no objects of the type may be declared; an abstract type may have abstract subprograms (these have no body). Abstract types and subprograms form a foundation from which concrete types may be derived.

Protected Type. Protected types are composite types that provide synchronized access to their inner components via a number of protected operations. Objects of protected types are passive and do not have a distinct thread of control; the mutual exclusion is provided automatically.

Task Type. Task types are composite types which are used to define active units of processing. Each object of a task type has its own thread of control.

Type Compatibility & Conversion

Explicit Type Conversion. Explicit type conversion is permitted only between closely related types.
  • Numeric types - conversion is permitted between any two numeric types. The conversion of a real type to an integer type rounds to the nearest integer.
  • Derived types - conversion is permitted between a derived type and its parent, or between two derived types that have the same parent.
  • Array types - conversion is permitted between two array types if the following conditions are met:
    1. both types have the same dimensions
    2. the index types are the same, or can be explicitly converted
    3. the component types are the same
In general, if a conversion from one type to another is permitted, then the inverse conversion is also permitted.

Here are some example type declarations:

 type count   is range 1..5000;
 type current is delta 0.025 range -100.0..100.0;
 type voltage is delta 0.01 range -500..500.0;
 type power   is digits 6;
 type weight  is digits 6;
 type matrix3 is array(1..3, 1..3) of float;
 type index   is new count;
 type rot_matrix is new matrix3;
 type vector  is array(integer range <>) of float;
 subtype vector6 is vector(1..6);
 type long_vector is array(1..36) of float;
 no_rounds   : count;
 reading     : index;
 cur_meas    : current;
 volt_meas   : voltage;
 rotate_1    : matrix3;
 rotate_2    : rot_matrix;
 temp_vector : long_vector;
Using the type declarations above, the following examples illustrate explicit type conversion:
 integer(cur_meas)         - real value is rounded 
                             to nearest integer
 weight(3 * no_rounds)     - integer value is 
                             converted to floating
                             point
 index(no_rounds)          - converts value of type
                             count to value of type
                             index. Both count and 
                             index are distinct
                             integer types.
 power(cur_meas*volt_meas) - converts universal_
                             fixed result to float-
                             ing point type power.
The above conversions would occur as part of an assignment statement.

Implicit Type Conversion. There are som subtle implicit type conversions in Ada, relating to the anonymous predefined types universal_integer, universal_real, and universal_fixed. Universal types in Ada are predefined anonymous types with no bounds, and for universal_real or universal_fixed types, an infinite precision. Obviously, these data types are conceptual in nature and values of these universal types must be converted to some numeric type before they can be assigned. Such conversion is implicit in the case of a numeric literal, a named number, or an attribute. In other cases, the conversion must be explicit (as described above).

Using the previous type declarations, the following list illustrates several implicit type conversions:

 index := 32;         - conversion from universal_
                        integer to integer type
                        count.
 flow_rate := 0.8;    - conversion from universal_
                        real to floating point type
                        value.

Assignment Statements

An assignment statement causes the value of an identifier or variable to be replaced by that of an expression of the same type. Assignment is normally performed by a simple bit copy of the value provided by the expression. However, in the case of nonlimited controlled types, assignment can be redefined by the user.
 Zero := 0;
 C := Zero;
 C := C + 1;
 Index := 25;
The combination := can be read as "gets the value of." Thus, the last example above can be read as, "Index gets the value of 25." The equal sign alone is reserved for another use. Actually, if we say that Index = 25, it is only mathematically correct if Index is never changed, but since it is a variable and will be changed, the equality is not true.

Constants

Constants should be used in a program wherever there is a value which is not going to change during program execution. In fact, if you attempt to change the value of a constant during program execution, Ada will give you an error message at compile time.

A constant must have an initial value assigned to it when it is declared. The examples below illustrate the syntax for declaring and initializing constants.

 Dozen  : constant integer := 12;
 Gross  : constant integer := 12 * Dozen;
 Big_No : constant         := 32_24_7;
 Two    : constant         := Big_No - 3_22_45;
The following list illustrates some meaningful names for constants, their values and their type.
 Pi               3.14159     float
 MilesToYards     1760        integer
 MilesToFeet      5280        integer
 Weeks_In_Year    52          integer
 Labor_Hours      2080        integer
 NumStudents      50          integer
 LitersToPints    0.568       float
 PintsToLiters    1.7605      float
 RunAgain         'y'         character
 User_Name        "dsmith"    string
Constants have two advantages. The first is that where they are used in calculations, the name of the constant can be used in place of the value allowing the meaning of the program to become clearer:
 EnglishVolume := MetricVolume * 0.568;
 EnglishVolume := MetricVolume * LitersToPints;
Without fully understanding the meanings of these statements, it should be clear that the second version of the assignment statement above is more meaningful than the first version.

The second advantage of constants is that if the value of a constant needs to be changed, it can be accomplished with only a single change to the program text. For the example above, if the number of students is increased to 75, then only the declaration of the constant NumStudents will have to be changed. The program should be written so that changing the value in the constant declaration will result in other changes in the program, but will ensure that the program still works correctly.

One of the principles which is used to differentiate between well written programs and poorly written programs is the ease with which the program can be modified when a change is required. The appropriate use of constants will allow such modifications to be more easily accomplished.

Constants are only constant during program execution. In the examples above some of the constants will never change, for example the number of yards in a mile. Other constants may change during the lifecycle of the application. For example, greater accuracy may be required in the value of Pi, or the number of students in a course may increase. However, while a program is executing the value of a constant used by the program cannot be changed.

Expressions

An expression is a program element formed of operations and operands that can be evaluated to yield a value. In Ada, an expression has a type which depends upon the operands and operations contained in the expression. Ada's strong typing riles apply both in the evaluation of an expression and in the assignment of the resulting value.
 pi         : constant := 3.141_592_655;
 sum        : float;
 line_count : integer;
 vel        : float;
 evaluation : threat;
 hex_count  : hexdigit;
 armed      : boolean;
The term primaries is used to refer to the fundamental constituents of an expression. The only names permitted are named numbers, attributes that yield values, and names denoting objects or denoting values. Here are some examples of primaries:
 pi              - a named number
 sum             - a name for an object
 integer'last    - an attribute
 4.7             - a numeric literal
 null            - the value null
 (1..25 => '*')  - an array aggregate
 (line-count+1)  - a parenthesized expression
 new threat      - an allocator
The following list illustrates a few example Ada expressions which are based on the declarations immediately above:
 vel                  - a primary
 not armed            - a factor
 16.2 * vel           - a term
 4.7                  - a simple expression
 -3.2 * vel           - a simple expression
 vel**2 + 10.0 * sum  - a simple expression
 line_count in 3..20  - a relation
The symbols used by Ada to represent the basic arithmetic operators are shown below. They are listed in order of their priority (top to bottom).

abs absolute value
** exponentiation
/ division
* multiplication
mod modulus
rem remainder
+ addition
- subtraction

Comparisons are made with the use of Ada relational operators. The table below summarizes these operators.

= equals
/= not equal to
> greater than
>= greater than or equal to
< less than
<= less than or equal to

The program below illustrates a number of Ada comparisons using Boolean and integer variables.

 with Ada.Text_IO;
 use Ada.Text_IO;
 procedure Compare is
   package Enum_IO is 
        new Ada.Text_IO.Enumeration_IO(BOOLEAN);
   use Enum_IO;
   One   : INTEGER := 1;
   Two   : INTEGER := 2;
   Three : INTEGER := 3;
   Is_It : BOOLEAN := TRUE;    -- initialized
   Which : BOOLEAN;            -- uninitialized
 begin
   Which := TRUE;
   Put("Which now has the value of ");
   Put(Which);
   New_Line;
   Which := FALSE;
   Put("Which now has the value of ");
   Put(Which);
   New_Line;
   Is_It := (One + 1) = Two;
   Is_It := One /= Two;
   Is_It := One + Two >= Three;
 end Compare;
    Click on the button at left to return to the calling page.

 

There have been visitors since 11/26/2003

Added to the Web: October 20, 1999.
Last updated: October 27, 1999.
Web page design by Dan Solarek.

http://cset.sp.utoledo.edu/cset4250/