120 likes | 213 Views
Polymorphism. Programming Language Design and Implementation (4th Edition) by T. Pratt and M. Zelkowitz Prentice Hall, 2001 Section 7.3. Polymorphism. The use of parameters to subprograms is one of the oldest characteristics of programming languages
E N D
Polymorphism Programming Language Design and Implementation (4th Edition) by T. Pratt and M. Zelkowitz Prentice Hall, 2001 Section 7.3
Polymorphism The use of parameters to subprograms is one of the oldest characteristics of programming languages But they have an l-value. That is, they are a data object that requires storage. Polymorphism means a single operator or subprogram name can to refer to any of a number of function definitions depending on the data types of the arguments and results. • Similar to overloading • It is generally applied to functions where a type is one of the arguments.
Polymorphism in ML reverse([1,2,3]); val it = [3,2,1]: int list reverse([1.0, 2.0, 3.0]); val it = [3.0,2.0,1.0]: real list; reverse([1,2,3], [4,5,6], [7,8,9]]; val it = [[7,8,9],[4,5,6],[1,2,3]] : int list list; Arguments to reverse are different list data types fun identity(x) = x; fn: 'a -> 'a identity(1); val it = 1: int; identity(1.5); val it = 1.5: real; But why is the following not polymorphic (or legal) in ML? fun add(x,y) = x+y?
Type inference in ML ML is strongly typed, but if it can infer a type, it does not need an explicit type declaration: fun add(x:int, y:int):int = x+y; add is fully qualified. Any one declaration defines the operation. All are equivalent: fun add(x:int, y) = x+y; fun add(x, y:int) = x+y; fun add(x, y):int = x+y; But fun add(x, y) = x+y; is ambiguous At runtime, function is well defined, but ML uses static type inferencing to determine function to invoke before execution.
Type inference examples ML does not allow automatic coersions between int and real, so what are signatures (if any) to the following? fun add(x,y) = real(x) + y; fun thing(x::y) = x+ hd(y); fun thing(x::y) = truncate(x)+ hd(y); fun thing(x::y:int list) = x+ hd(y);
Polymorphism in arrays Note: Implementation of arrays differs in most languages: Pascal - arrays are explicit and bounds are part of type. Not polymorphic. type matrix10 = array[1..10] of integer; var x: matrix10; Ada - polymorphic array bounds type matrix is array (integer range <>) of integer; y: matrix(1..30); C arrays don't really exist - shorthand for pointer variables. int C[10]; C[5] means *C+5
Create polymorphism Can often build parameterized objects for true polymorphism, e.g., stack of objects: Y: stack(int, 10) up to 10 ints Z: stack(float, 20) up to 20 reals Can do this in ML and Ada for any data types that don't involve calculations (i.e., only for operations that use pointer manipulation (stacking and unstacking) • Why? Macros can be used to simulate this for languages without polymorphism, e.g. In Pascal: sum(int,A,B) can be implemented as a macro with 4 sequences depending upon types of arguments: trunc(A)+B A+trunc(B) A+B trunc(A)+trunc(B)
Implementation For statically typed languages (ML, C++), polymorphism is easy: Keep track of function argument types in symbol table of compiler. For dynamically typed languages: Two forms of arguments can be passed to a polymorphic function: 1. An immediate descriptor - when the value to a function is smaller than the size of the fixed field. For example, passing a Boolean, character, or small integer to a function uses less space than the fixed-size object permits. The actual value is placed in the argument location, and the extra bits in the field are used to tell the function what the type actually is. 2. A boxed descriptor occurs in all other cases.
Boxed descriptors The argument field will contain a type indicator stating the argument is boxed. • The rest of the field will be the address of the actual object (which will be elsewhere, such as in heap storage). • At this address, the complete type information will be given, such as giving the entire structure of the composite data object.
Example of dynamic polymorphism Assume argument descriptor field is 5 bytes. Byte 1 is a data type indicator and Bytes 2 to 5 are the data. The following arguments can be passed to a polymorphic function: 1. 32-bit Integer data. Byte 1=0 signifying integer, bytes 2 to 5 are the 32-bit integer value. 2. 8-bit character data. Byte 1=1 signifying character, byte 2= actual character argument, bytes 3 to 5 unused. 3. 1-bit Boolean data. Byte 1=2 and byte 2= 0 or 1. 4. Complex record structure. Byte 1=3 and bytes 2 to 5 are pointer to structure. The r-value at this pointer address contains more information about the actual argument.