If you have not already done so, please complete the on-line course evaluation for your classes by visiting my.wsu.edu.
Types
CptS 355 - Programming Language Design Washington State University |
|||||
TypesWhat is a type:
Importance
What is a type error?An inconsistent use of the bit sequence representing a value. An attempt to use a value in an operation inconsistent with the value's type.Some type errors may cause hardware exceptions. If you could write
Use of types in compilation
Use of types at run-time
Type Safety: strong typingType safety, or strong typingis assurance by a language that no program can use a value in a manner inconsistent with its type without causing either a compile time error (static strong typing) or run-time error (dynamic strong typing) Sources of type unsafety:
Run-time (dynamic) versus Compile-time (static) type checking
Type checking and type inference algorithms
fun f (g, h) = g (h 0)
'e 'c 'a 'c 'a int
-------
'b
----------
'd
where,
'a = int -> 'b
'c = 'b -> 'd
'e = ('c * 'a) -> 'd
= ('b -> 'd) * (int -> 'b) -> 'd
So the final type for function f
is ('b -> 'd) * (int -> 'b) -> 'd.
Note that in the book, the type for this function is inferred to be
('a -> 'b) * (int -> 'a) -> 'b
which is the same type. That is, if by consistently renaming the type variables
in one type you can produce the other type, then they are the same type.
Type correctnessIf equations derived in type inference are solvable, the program is type correct, otherwise not.A program can be type-correct but may still compute the incorrect result. Sometimes the inferred type of a function will suggest that something is wrong. For example:
'b list 'a list
fun reverse [] = []
| reverse (x::xs) = reverse xs;
'b 'b list 'b list
----------- ------------
'b list 'a list
'b list -> 'a list
in this function that is supposed to reverse a list the fact that the inferred type
is 'b list -> 'a list suggests that the function's implementation
is incorrect: we would certainly expect that a function for reversing lists would not change the type of the elements!
PolymorphismPolymorphic means "having multiple forms." (The opposite is monomorphic.) In ML both functions and datatypes may be polymorphic. An example polymorphic function type:'a list -> 'a. 'a
here is called a type variable and the thing to remember is that in a type,
each occurrence of a given type variable must be replaced by the same type. So, the
type int list -> int is an instance of 'a list -> 'a but
int list -> real is not.
An example of a polymorphic datatype is
datatype 'a Tree = Leaf of 'a | Interior of ('a Tree * 'a Tree)
ML type declarations may also be polymorphic, but
type declarations just introduce new names for types that you could write anyway
(see "Type declarations and type equality" below)
type ('a,'b) Pair = 'a * 'b
Three kinds of polymorphism in programming languages
Implementations of parametric polymorphismConsider a C++ template function and some code that uses it:
template <typename T>
void swap (T& x, T& y) {
T tmp = x; x = y; y = tmp;
}
int i, j;
float a, b;
swap(i,j);
swap(a,b);
In C++ the compiler/linker will generate code implementing swap for each different
type on which swap is used. This is necessary because C++ values can require different
amounts of storage and space must be allocated for the tmp variable. The
result is that use of templates in C++ can result in a lot of code being generated to
handle all the different cases.
Let's look at the swap in ML. Remember that ML doesn't have variables but does have ref values. Swap only makes sense for ref values. fun swap (x,y) = let val tmp = !x in x:= !y; y := tmp end(Note that the ML swap code on p. 149 of the book is incorrect. Make sure you know why.) ML uses uniform data representation which means that all values take the same amount of space. (The implementation uses hidden pointers to make this possible.) As a result only a single body of machine code has to be generated for a polymorphic ML function, no matter how many different types it is used for. On the other hand, there is a time and space cost associated with uniform data respresentations because more values have to be dynamically allocated and referring to them may require an extra pointer dereference. OverloadingConsider the expressions3+2 3.0+2.0In both C and ML the '+' operation used in these two expressions is different, the first referring to the integer + operation, the latter to the floating point + operation. The compiler simply looks for the function having the given name that matches the types of the arguments. This is called resolving the overloading.
In C, but not ML, you can also write Type declarations and type equalityThe basic question here is "does declaring a type create a brand new type, different from all other types, or does it create a type that is the same as some other type?" The question is answered differently in different languages and even for different kinds of declarations within a single language. In ML, datatype declarations introduce new types, but type declarations merely introduce new names for existing types. Considertype Celsius = real; type Fahrenheit = real val cTemp : Celsius = 30.0; val fTemp : Fahrenheit = 30.0; if fTemp=cTemp ... (* type correct but algorithmically wrong *)Schemes (such as type declarations in ML and typedefs in C) in which declarations only give new names to existing types are said to be transparent. Another term for this is structural type equality. Consider now: datatype celsiusTemp = C of real datatype fahrenheitTemp = F of real val cTemp = C of 30.0 val fTemp = F of 30.0 if ftemp=cTemp ... (* type error *)Schemes (such as datatype declarations in ML and struct defs in C) in which declarations introduce totally new types are termed opaque, another term being name type equality. |
|||||