Ordinal Data Types in Delphi

Creative businesswoman explaining to coworker
Klaus Vedfelt/Taxi/Getty Images

Delphi's programming language is an example of a strongly typed language. This means that all variables must be of some type. A type is essentially a name for a kind of data. When we declare a variable we must specify its type, which determines the set of values the variable can hold and the operations that can be performed on it.

Many of Delphi's built-in data types, such as Integer or String, can be refined or combined to create new data types.

In this article, we'll see how to create custom ordinal data types in Delphi.

Ordinal Types

The defining characteristics of ordinal data types are: they must consist of a finite number of elements and they must be ordered in some way. 

The most common examples of ordinal data types are all the Integer types as well as Char and Boolean type. More precisely, Object Pascal has twelve predefined ordinal types: Integer, Shortint, Smallint, Longint, Byte, Word, Cardinal, Boolean, ByteBool, WordBool, LongBool, and Char. There are also two other classes of user-defined ordinal types: enumerated types and subrange types.

In any ordinal types, it must make sense to move backward or forward to the next element. For example, real types are not ordinal because moving backward or forward doesn't make sense: the question "What is the next real after 2.5?" is meaningless.

Since, by definition, each value except the first has a unique predecessor and each value except the last has a unique successor, several predefined functions are used when working with ordinal types:

Ord(X)Gives the index of the element
Pred(X)Goes to the element listed before X in the type
Succ(X)Goes to the element listed after X in the type
Dec(X;n)Moves n elements back (if n is omitted moves 1 element back)
Inc(X;n)Moves n elements forward (if n is omitted moves 1 element forward)
Low(X)Returns the lowest value in the range of the ordinal data type X.
High(X)Returns the highest value in the range of the ordinal data type X.

For example, High(Byte) returns 255 because the highest value of type Byte is 255, and Succ(2) returns 3 because 3 is the successor of 2.

Note: If we try to use Succ when at the last element Delphi will generate a run-time exception if the range checking is on.

Enumerated Data Types

The easiest way to create a new example of an ordinal type is simply to list a bunch of elements in some order. The values have no inherent meaning, and their ordinality follows the sequence in which the identifiers are listed. In other words, an enumeration is a list of values.

type TWeekDays = (Monday, Tuesday, Wednesday,
                 Thursday, Friday, Saturday, Sunday);

Once we define an enumerated data type, we can declare variables to be of that type:

var SomeDay : TWeekDays;

The primary purpose of an enumerated data type is to make clear what data your program will manipulate. An enumerated type is really just a shorthand way of assigning sequential values to constants. Given these declarations, Tuesday is a constant of type TWeekDays.

Delphi allows us to work with the elements in an enumerated type using an index that comes from the order that they were listed in. In the previous example: Monday in the TWeekDays type declaration has the index 0, Tuesday has the index 1, and so on.

The functions listed in the table before let us, for example, use Succ(Friday) to "go to" Saturday.

Now we can try something like:

for SomeDay := Monday to Sunday do
 if SomeDay = Tuesday then
  ShowMessage('Tuesday it is!');

The Delphi Visual Component Library uses enumerated types in many places. For example, the position of a form is defined as follows:

TPosition = (poDesigned, poDefault, poDefaultPosOnly,
             poDefaultSizeOnly, poScreenCenter);

We use Position (through the Object Inspector) to get or set the size and placement of the form.

Subrange Types

Simply put, a subrange type represents a subset of the values in another ordinal type. In general, we can define any subrange by starting with any ordinal type (including a previously defined enumerated type) and using a double dot:

type TWorkDays = Monday .. Friday;

Here TWorkDays includes the values Monday, Tuesday, Wednesday, Thursday, and Friday.

That's all -- now go enumerate!