Types

Types

There are four built-in data-types, and a user may define their own types:

object atom integer sequence user defined

Specifying The Type of a Variable

So far you've already seen some examples of variable types but now we will describe the use of types more precisely.

Variable declarations have a type name followed by a list of the variables being declared. For example,

object a 
 
global integer x, y, z 
 
procedure fred(sequence q, sequence r) 

The types: object, sequence, atom and integer are predefined. Variables of type object may take on any value. Those declared with type sequence must always be sequences. Those declared with type atom must always be atoms.

Variables declared with type integer must be atoms with integer values from -1073741824 to +1073741823 inclusive. You can perform exact calculations on larger integer values, up to about 15 decimal digits, but declare them as atom, rather than integer.

Note:
In a procedure or function parameter list like the one for fred() above, a variable type name may only be followed by a single parameter identifier.
Performance Note:
Calculations using variables declared as integer will usually be somewhat faster than calculations involving variables declared as atom.

User-Defined Types

A type may be described as "an abstract description of values." That means there is an algorithm that can test a value to determine if it is valid for its intended use. Each time a "typed value" is used, it is tested first--a pass means the program continues, a fail means an error message is issued.

The Euphoria user type system is there as a convenience, not as a requirement. It can even be turned on and off; for the entire program, or for only a selected block.

To augment the predefined types, you can create user-defined types. Define a single-parameter function, but declare it with type ... end type instead of *function ... end function. For example:

type hour(integer x) 
    return x >= 0 and x <= 23 
end type 
 
hour h1, h2 
 
h1 = 10      -- ok 
h2 = 25      -- error! program aborts with a message 

Variables h1 and h2 can only be assigned integer values in the range 0 to 23 inclusive. After each assignment to h1 or h2 the interpreter will call hour(), passing the new value. The value will first be checked to see if it is an integer (because of "integer x"). If it is, the return statement will be executed to test the value of x (i.e. the new value of h1 or h2). If hour() returns 1 true, execution continues normally. If hour() returns 0 false then the program is aborted with a suitable diagnostic message.

A user type like "hour" can be used to declare subroutine parameters as well:

procedure set_time(hour h) 

set_time() can only be called with a reasonable value for parameter h, otherwise the program will abort with a message.

A variable's type will be checked after each assignment to the variable (except where the compiler can predetermine that a check will not be necessary), and the program will terminate immediately if the type function returns false. Arugments to a routine are checked agains the type in the parameter list, each time that the routine is called. This checking guarantees that a variable can never have a value that does not belong to the type of that variable.

Unlike other languages, the type of a variable does not affect any calculations on the variable, nor the way its contents are displayed. Only the value of the variable matters in an expression. The type just serves as an error check to prevent any "corruption" of the variable. User-defined types can catch unexpected logical errors in your program. They are not designed to catch or correct user input errors. In particular, they cannot adjust a wrong value to some other, presumably legal, one.

Type checking can be turned off or on between subroutines using the with type_check or without type_check (see special statements). It is initially on by default.

Note to Bench markers:
When comparing the speed of Euphoria programs against programs written in other languages, you should specify without type_check at the top of the file. This gives Euphoria permission to skip run-time type checks, thereby saving some execution time. All other checks are still performed, e.g. subscript checking, uninitialized variable checking etc. Even when you turn off type checking, Euphoria reserves the right to make checks at strategic places, since this can actually allow it to run your program faster in many cases. So you may still get a type check failure even when you have turned off type checking. Whether type checking is on or off, you will never get a machine-level exception. You will always get a meaningful message from Euphoria when something goes wrong. (This might not be the case when you poke directly into memory, or call routines written in C or machine code.)
Euphoria's method of defining types is simpler than what you will find in other languages, yet Euphoria provides the programmer with greater flexibility in defining the legal values for a type of data. Any algorithm can be used to include or exclude values. You can even declare a variable to be of type object which will allow it to take on any value. Routines can be written to work with very specific types, or very general types.

For many programs, there is little advantage in defining new types, and you may wish to stick with the four predefined types. Unlike other languages, Euphoria's type mechanism is optional. You don't need it to create a program.

However, for larger programs, strict type definitions can aid the process of debugging. Logic errors are caught close to their source and are not allowed to propagate in subtle ways through the rest of the program. Furthermore, it is easier to reason about the misbehavior of a section of code when you are guaranteed that the variables involved always had a legal value, if not the desired value.

Types also provide meaningful, machine-checkable documentation about your program, making it easier for you or others to understand your code at a later date. Combined with the subscript checking, uninitialized variable checking, and other checking that is always present, strict run-time type checking makes debugging much easier in Euphoria than in most other languages. It also increases the reliability of the final program since many latent bugs that would have survived the testing phase in other languages will have been caught by Euphoria.

Anecdote 1:
In porting a large C program to Euphoria, a number of latent bugs were discovered. Although this C program was believed to be totally "correct", we found: a situation where an uninitialized variable was being read; a place where element number "-1" of an array was routinely written and read; and a situation where something was written just off the screen. These problems resulted in errors that weren't easily visible to a casual observer, so they had survived testing of the C code.
Anecdote 2:
The Quick Sort algorithm presented on page 117 of Writing Efficient Programs by Jon Bentley has a subscript error! The algorithm will sometimes read the element just before the beginning of the array to be sorted, and will sometimes read the element just after the end of the array. Whatever garbage is read, the algorithm will still work--this is probably why the bug was never caught. But what if there isn't any (virtual) memory just before or just after the array? Bentley later modifies the algorithm such that this bug goes away--but he presented this version as being correct. Even the experts need subscript checking!
Performance Note:
When typical user-defined types are used extensively, type checking adds only 20 to 40 percent to execution time. Leave it on unless you really need the extra speed. You might also consider turning it off for just a few heavily-executed routines. Profiling can help with this decision.

Not Categorized, Please Help

Search



Quick Links

User menu

Not signed in.

Misc Menu