CS 334
|
Packages used to define abstract data types. Package together type, operations (& state) and hide rep. Provides support for parameterized packages (polymorphism)
package <package-name> is -- declarations of visible types, variables, constants, and subprograms private -- complete definitions of private types and constants end <package-name>; package body <package-name> is -- definitions of local variables, types, and subprograms, and complete bodies for -- subprograms declared in the specification part above. Code for initialization -- and exception handlers end <package-name>;Sample Program:
package VECT_PACKAGE is -- declarations only type REAL_VECT is array (INTEGER range <>) of float; function SUM(V: in REAL_VECT) return FLOAT; procedure VECT_PRODUCT(V1,V2 : in REAL_VECT) return FLOAT; function MAX(V: in REAL_VECT) return FLOAT; end VECT_PACKAGE ; package body VECT_PACKAGE is -- details of implementation function SUM(V: in REAL_VECT) return FLOAT is TEMP : FLOAT := 0.0; begin for I in V'FIRST..V'LAST loop TEMP:= TEMP + V(I); end loop; return TEMP; end; -- definitions of VECT_PRODUCT and MAX subprograms would appear here end VECT_PACKAGE ; with VECT_PACKAGE, TEXT_IO; -- used to make separately compiled package visible procedure MAIN is use VECT_PACKAGE, TEXT_IO; -- eliminates need for qualifiers package INT_IO is new INTEGER_IO(INTEGER); --instantiation of generic packages package REAL_IO is new FLOAT_IO(FLOAT); use INT_IO, REAL_IO; K: INTEGER range 0..99; begin loop GET(K); exit when K<1; declare -- start of block A : REAL_VECT(1..K); -- provides subscript bounds begin for J in 1..K loop GET(A(J)); PUT(A(J)); end loop; PUT("SUM = "); PUT(SUM(A)); -- uses package function end; -- of block end loop; end MAIN ;SOPHISTICATED (generic) STACK EXAMPLE
Stack represented internally in package (closer to object-oriented than get w/ Modula 2 below)
generic length : Natural := 100; -- generic parameters type element is private; -- only assignment and tests for = may be done on objects of "private" type -- "limited private" is also available. package stack is procedure push (X : in element); procedure pop (X: out element); function empty return boolean; stack_error : exception; end stack; package body stack is space : array (1..length) of element; top : integer range 0..length := 0; procedure push (X : in element) is begin if full() then raise stack_error; else top := top + 1; space(top) := X; end if; end push; procedure pop (X: out element) is begin if empty() then raise stack_error; else X := space(top); top := top - 1; end if; end pop; function empty return boolean is begin return (top = 0); end; function full return boolean is begin return (top = length); end; end stack;
Notice: Data structure of stack is entirely hidden from user - there is no object of type stack available to user.
How to use:
package stack1 is new stack(20,integer); package stack2 is new stack(100, character); -- Note that this initializes length in both cases to 0 use stack2; stack1.push(5) if not stack1.empty() then stack1.pop(Z); endif; push('z');Note: Package definition is very much like that of a record with procedures allowed as (non-updateable) fields. E.g.
stack = package push : procedure (X : in element); pop : procedure (X: out element); empty : function return boolean; stack_error : exception; end package;
One of two key ideas behind object-oriented programming.
Can also have package where user can manipulate objects of type stack (external representation) like in Modula-2.
Advantage to external rep. is that can define array of stack, etc.
generic length : Natural := 100; type element is private; -- generic parameters package stack is type stack is private; procedure make_empty (S : out stack); procedure push (S : in out stack; X : in element); procedure pop (S : in out stack; X: out element); function empty (S : stack) return boolean; stack_error : exception; private type stack is record space : array(1..length) of element; top : integer range 0..length := 0; end record; end stack; package body stack is procedure make_empty (S : out stack); begin S.top := 0; end make_empty ; procedure push (S : in out stack; X : element) is begin if full(S) then raise stack_error; else S.top := S.top + 1; S.space(S.top) := X; end if; end push; procedure pop (S : in out stack; X: out element) is begin if empty(S) then raise stack_error; else X := S.space(S.top); S.top := S.top - 1; end if; end pop; function empty(S : in out stack) return boolean is begin return (top = 0); end; function full(S : in out stack) return boolean is begin return (S.top = length); end; end stack;
Biggest problem is exposure of representation of ADT in private part of package spec.
Not available to user, but must recompile if change representation.
Important difference between the two definitions is that in the first, the ADT is accessible only via operations which belong to the object. Can think of the Stack as an "agent". In the second, there is a data type called stack, and the operations act upon the passive data. In the first one must ask the ADT nicely to perform an operation that it knows how to do. In the second, the operation is imposed on the passive data.
Modula 2 is 1980 revision (influenced by Mesa at Xerox PARC) intended to synthesize systems programming with general purpose language supporting modern software engineering.
(operating system for Lilith microcomputer written in Modula 2)
Sample program
DEFINITION MODULE stackMod; IMPORT element FROM elementMod; TYPE stack; PROCEDURE make_empty (VAR S : stack); PROCEDURE push (VAR S : stack; X : element); PROCEDURE pop (VAR S : stack; X: element); PROCEDURE empty (S : stack): BOOLEAN; END stackMod. IMPLEMENTATION MODULE stackMod; TYPE stack = POINTER TO RECORD space : array[1..length] of element; top : INTEGER; END; PROCEDURE make_empty (VAR S : stack); BEGIN S^.top := 0; END make_empty ; ... END stackMod;
Opaque types (those declared without definition in Definition module) must be pointers or take no more space than pointers.
Compare and contrast Modula-2 and Ada on supporting abstract data types.
Provides both packaging and hiding of representation
(cvt used to go back and forth btn external abstract type and internal representation).
May have parameterized clusters where specify needed properties of type paramenter. E.g.,
sorted_bag = cluster [t : type] is create, insert, ... where t has lt, equal : proctype (t,t) returns (bool);Abstraction facilities described in Liskov et al. paper in collection on reserve shelf.
Biggest difference from Ada and Modula-2 is that cluster is a type. Therefore can create multiple copies. Elements of cluster types are held as implicit references.
Provides for encapsulation and information hiding
Example:
abstype intstack = mkstack of (int list) with exception stackUnderflow val emptyStk = mkstack [] fun push (e:int) (mkstack(s):intstack) = mkstack(e::s) fun pop (mkstack([])) = raise stackUnderflow | pop (mkstack(e::s)) = mkstack(s) fun top (mkstack([])) = raise stackUnderflow | top (mkstack(e::s)) = e fun IsEmpty (mkstack([])) = true | IsEmpty (mkstack(e::s)) = false end;
Generic stacks ADT:
abstype 'a stack = mkstack of ('a list) with exception stackUnderflow val emptyStk : 'a stack = mkstack [] fun push (e:'a) (mkstack(s):'a stack) = mkstack(e::s) fun pop (mkstack([]):'a stack) = raise stackUnderflow | pop (mkstack(e::s):'a stack) = mkstack(s):'a stack fun top (mkstack([]):'a stack) = raise stackUnderflow | top (mkstack(e::s):'a stack) = e fun IsEmpty (mkstack([]):'a stack) = true | IsEmpty (mkstack(e::s):'a stack) = false end;
Cannot get at representation of stack
Reference to mkstack(l) will generate an error message.
Only access through constants and op's.
More sophisticated support through modules, which also support separate compilation. See Ullman's text for more details.
In Pascal get extreme position that procedures only take arrays w/exactly same dimensions as well as types.
Modula-2 starts opening up, by allowing different sizes of arrays as parameters, but still require same base type.
Restrictive since sort procedures will work with any ordered type.
Rather work with "polymorphic" functions and procedures.
Two main flavors of polymorphism: ad hoc and parametric (talk about subtype later).
Ad hoc also known as overloading. Done well it can make code easier to understand. E.g., "+" for real and integer addition.
Done poorly it can confuse and lead to errors, e.g. "+" for string concatenation.
Most languages provide some overloading (e.g., arith. operators, I/O), a few (e.g., Ada and C++) allow user to define overloaded operations (either infix or prefix).
E.g. in Ada, write
function "+"(m,n:Complex) return Complex is begin ... end;
Must provide a mechanism to support resolution of overloading - i.e. which actual operation does an instance refer to.
Two major ways of resolving:
function "+"(m,n:real) return Complex is ... end;Then, in "c + (3.3 + 4.1)", inner addition will return complex if "c" is complex and will return real if "c" is real.
Overloading is not essential and is easily abused. Useful when matches expectations from external notation. Confusing and unhelpful otherwise.
Parametric polymorphism usually considered a good thing.
These are almost always related to parameterized types:
Can define parameterized types through datatype definitions.
E.g., datatype 'a stack = Empty | Push of 'a * ('a stack)
Languages support two different forms of parametric polymorphism:
implicit and explicit.
Differ on whether must supply type parameter.
ML is example of implicit polymorphism. Needn't supply type of any
expression. System derives most general type, often polymorphic.
E.g., reverse: 'a list -> 'a list.
Very clever type inference algorithm invented independently by Hindley and
Milner.
Collects all information about types of terms in expression (assigning a
variable if no info) and then tries to "unify" all information so mutually
consistent.
E.g.
fun map f nil = nil | map f (hd::tl) = (f hd):: (map f tl) map: 'a -> 'b -> 'c
Overloading does not interact well with implicit polymorphism because often don't have type info necessary to disambiguate expressions.
Some ML designers (e.g., Harper) feel that it would have been better to omit overloading of built-in functions.