Declaration equivalence: types whose declarations lead back to the same original type expression by a series of redeclarations (of the form type t1 = t2, or typedef TE tn). Pascal, Modula use decl equivalence In C, decl equiv used for structs and unions, structual equivalence for other types such as pointers and arrays. struct { int a ; float b ; } x ; struct { int a ; float b ; } y ; then x and y are structure equivalent but not declaration equivalent. typedef int* intp ; typedef int** intpp ; intpp v1 ; intp *v2 ; then v1 and v2 are structure equivalent. Type compatibility : Weaker notion than type equivalence. Type compatibility rules are different for different operators. We give an example here, corresponding to the assignment operator. v = expr is meaningful if type of is compatible with type of v. Compatibility in this case is based on a subtype relationship, i.e., if the type of expr is a subtype of the type of v, then there is compatibility. Example: in most languages, assigning integer value to a real variable is permitted, since integer is a subtype of real. in OO-languages, an object of a derived type can be assigned to an object of the base type. Procedure parameter passing uses the same notion of comptibility as assignment. (There may be exceptions to this in some languages.) This is because a procedure call can be though of as proceding in two steps: -- assignment of actual parameter expressions to the formal parameters of the procedure -- execution of the procedure body Note: formal parameters are the parameter names that appear in the function declaration. The actual parameters are the expressions that appear at the point of function call. Type checking -------------- Static (compile time) : Benefits - no run-time overhead - programs safer/more robust Dynamic (run-time) : Disadvantages -- runtime overhead -- maintaining type info at runtime -- performing type checks at runtime Benefits - more flexible / more expressible e.g., C++ allows casting of subclass to superclass (always type-safe). superclass to subclass (not necessarily type-safe). Since C++ is statically typed, there is no way to check if the second form of casting is safe at runtime. In contrast, Java uses a combination of static and dynamic type-checking. This enables Java to check the potentially unsafe casts at runtime to see if they are OK. Strongly typed language: programs in such languages will execute without producing uncaught type errors at runtime e.g., no invalid memory access -- no seg fault -- array index out of range -- access of null pointer Type checking relies on type compatibility and type inference rules. Type inference rules are used to infer types of expressions. e.g., type of (a+b)+c is inferred from type of a, b and c and the inference rule for operator + Type inference rules typically operate on a bottom-up fashion. Given type of e1 and w2, the fule specifies the type of e1 op e2 for each operator op. e.g., +:real /\ / \ / \ / \ +:real c:real /\ / \ / \ / \ a:int b:real In SML, type inference rules cature bottom-up as well as top-down flow of type info. Top-down: fun f x y = (x+y): real f:real /\ / \ / \ / \ x:real y:real Here types of x and y are inferred from return type of f (real). Most of the time SML programs don't require type declaration. Type conversion EXPLICIT: Functions are used to perform conversion. strtol, atoi, itoa in C; real and int etc. IMPLICIT conversion (coercion) e.g., if a is real and b is int then type of a+b is real Before doing the addition, b must be converted to a real value. This conversion is done automatically. Casting (as in C) Invisible conversion: in unions If you store a value in the integer field of a union, but then access a float field, then you will get unpredictable results: it would be the hardware representation of an integer interpreted as a floating point number. Summary: We covered all of Chapter 6 in the textbook, excluding sections 6.3.3, 6.3.4, 6.3.7, 6.4.1, 6.4.2.