Why is it so important to give each value a type? There are several answers to this question.
(1) The implementation of operations like addition is different depending on the types of the operands.
(2) If the programmer gives operands of the wrong type, this is an error that should be reported to her or him.
(3) The type of a variable is important documentation about the software. For example, it is very useful to know whether <2.5,3.7> is of type rectangular or polar, where
type rectangular = record x, y: real end;
type polar = record r, theta: real end;
Case 1: Think of the rectangular and polar types above, and consider a procedure that displays a point on the screen:
Should it be legal to call plot(q) where q is a variable of type polar? Intuitively, the plot procedure will probably do the wrong thing with q.procedure plot (p: rectangular);
begin
(* code *)
end;
Case 2: Consider a procedure that just writes out a tuple for debugging purposes:
Intuitively, this procedure should be a legal operation to perform on variables of type rectangular and also of type polar.procedure debugwrite (v: record x,y: real end);
begin
writeln(x,y);
end;
Satisfying the first case is called name equivalence for types. Two variables are defined to have equivalent types if and only if they have literally the same type, i.e. the names of their types are the same.
Example: Consider
Here a and b have equivalent types, and so do c and d, but alpha is not equivalent to the type of c and d.type alpha = array [1..10] of real;var a,b: alpha;
c,d: array [1..10] of real;
Satisfying the second case is called structural equivalence for types. Two variables are defined to have equivalent types if their types are constructed in similar ways.
The definition of structural equivalence needs to be made more concrete. It is actually a recursive definition:
The most common rule is that values of type integer can be converted to values of type real when necessary. This is called coercion.
This rule can be ambiguous: consider <missing example>
Note that coercion is different from overloading, where the same name is used for different operations. For example the minus sign is usually overloaded to mean four different operations: integer and real negation (a unary operation) and subtraction (a binary operation).
A programming language can have overloading without
having coercion!
For example in Pascal, consider
Here smallint is a subtype of integer, so despite name equivalence, all integer operations can be used on x.type smallint = -256 .. 255;
var x: smallint;
Similarly, an object-oriented class should have name equivalence. But by default a class method should also be available for objects of a child class. This is a form of overloading.
In many OO PLs the programmer can choose the visibility
of methods, i.e. choose which overloading is allowed. This really
allows the programmer to choose the exact notion of type equivalence to
use for each class, which makes some language mechanisms (subtypes for
example) redundant. A subtype is really just a derived class.
Definitions:
An identifier is a name that denotes a value which
is obtained by "looking up" the declaration of the identifier. Typically
identifiers can be either constants or variables. Parameter identifiers
can be either also.
Depending on the language, strings may or may not be legal literals, and may or may not be written differently from characters.2.5E2
'!'
"prompt?"