CS 334
|
Support alternatives w/in type:
Ex.
RECORD name : string; CASE status : (student, faculty) OF student: gpa : real; class : INTEGER; | faculty: rank : (Assis, Assoc, Prof); END; END;
Save space yet (hopefully) provide type security. Saves space because the amount of space reserved for a variable of this type is the larger of the variants.
Fails in Pascal / MODULA-2 since variants not protected.
How is this supported in ML?
datatype IntReal = INTEGER of int | REAL of real;Can think of enumerated types as variant w/ only tags!
NOTICE: Type safe. Clu and Ada also support type-safe case for variants:
Ada: Variants - declared as parameterized records:
type geometric (Kind: (Triangle, Square) := Square) is record color : ColorType := Red ; case Kind of when Triangle => pt1,pt2,pt3:Point; when Square => upperleft : Point; length : INTEGER range 1..100; end case; end record; ob1 : geometric -- default is Square ob2 : geometric(Triangle) -- frozen, can't be changedAvoids Pascal's problems w/holes in typing.
Illegal to change "discriminant" alone.
ob1 := ob2 -- OK ob2 := ob1 -- generate run-time check to ensure TriangleIf want to change discriminant, must assign values to all components of record:
ob1 := (Color=>Red,Kind=>Triangle,pt1=>a,pt2=>b,pt3=>c);
If write code
... ob1.length...then converted to run-time check:
if ob1.Kind = Square then ... ob1.length .... else raise constraint_error end if.
Fixes type insecurity of Pascal
Note disjoint union is not same as set-theoretic union, since have tags.
IntReal = {INTEGER} x int + {REAL} x real
C supports undiscriminated unions:
typedef union {int i; float r;} utype.As usual with C, it is presumed that the programmer knows what he/she is doing and no static or run-time checking is performed.
Mapping from index type to range type
E.g. Array [1..10] of Real corresponds to {1,...,10} -> Real
Operations and relations: selection ". [.]", :=, =, and occasionally slices.
E.g. A[2..6] represents an array composed of A[2] to A[6]
Index range and location where array stored can be bound at compile time, unit activation, or any time.
For instance, in Pascal, an array stored in a local variable is allocated on the run-time stack, and its location on the stack may vary in different invocations of the procedure.
With semi-dynamic (or dynamic) arrays, the index set (and hence size) of the array may vary at run-time. For instance in ALGOL 60 or Ada, an array held in a local variables may have index bounds determined by a parameter to the routine. It is called semi-dynamic because the size is fixed once the routine has been activated.
A flexible array is one whose size can change at any time during the execution of a program. Thus, while a particular size array may be allocated when a procedure is invoked, the array may be expanded in the middle of a loop if more space is needed.
The key to these differences is binding time, as usual!
What is difference from an array? Efficiency, esp. w/update.
update f arg result x = if x = arg then result else f xor
update f arg result = fn x => if x = arg then result else f xProcedure can be treated as having type S -> unit for uniformity.
set of elt_type;Typically implemented as bitset or linked list of elts
Operations and relations: All typical set ops, :=, =, subset, .. in ..
Why need base set to be primitive type? What if base set records?
tree = Empty | Mktree of int * tree * treeIn most lang's built by programmer from pointer types.list = Nil | Cons of int * list
Sometimes supported by language (e.g. Miranda, Haskell, ML).
Why can't we have direct recursive types in ordinary imperative languages?
OK if use ref's:
list = POINTER TO RECORD first:integer; rest: list END;
Recursive types may have many sol'ns
E.g. list = {Nil} union (int x list) has following sol'ns:
Theoretical result: Recursive equations always have a least solution - though infinite set if real recursion.
Can get via finite approximation. I.e.,
list0 = {Nil}Very much like unwinding definition of recursive functionlist1 = {Nil} union (int x list0) = {Nil} union {(n, Nil) | n in int}
list2 = {Nil} union (int x list1) = {Nil} union {(n, Nil) | n in int} union {(m,(n, Nil)) | m, n in int}
...
list = Unionn listn
fact = fun n => if n = 0 then 1 else n * fact (n-1) fact0 = fun n => if n = 0 then 1 else undef fact1 = fun n => if n = 0 then 1 else n * fact0(n-1) = fun n => if n = 0, 1 then 1 else undef fact2 = fun n => if n = 0 then 1 else n * fact1(n-1) = fun n => if n = 0, 1 then 1 else if n = 2 then 2 else undef ... fact = Unionn factnNotice solution to T = A + (T->T) is inconsistent with classical mathematics!
datatype univ = Base of int | Func of (univ -> univ);
operations: hd, tail, cons, length, etc.
Persistent data - files.
Are strings primitive or composite?
var x : integer {bound at translation time}The variable can only hold values of that type. (Pascal/Modula-2/C, etc.)
FORTRAN has implicit declaration using naming conventions
In either case, run real danger of problems due to typos.
Example in ML, if
datatype Stack ::= Nil | Push of int;then define
fun f Push 7 = ...What error occurs?
Answer: Push is taken as a parameter name, not a constructor.
Therefore f is given type: A -> int -> B rather than the
expected: Stack -> B
Dynamic: Variables typically do not have a declared type. Type of value may vary during run-time. Esp. useful w/ heterogeneous lists, etc. (LISP/SCHEME).
Dynamic more flexible, but more overhead since must check type before performing operations (therefore must store tag w/ value).
Dynamic binding found in APL and LISP.
no characters or strings, no user-defined of any sort.
Arrays - at most 3-dim'l of built-in type. Subscripts begin at 1
Orig., restricted form of subscript expressions.
No records or sets. Many holes in typing.
Arrays of built-in types - no limit on dim'n, bounds any integers, semi-dynamic arrays
No records or sets. Strongly and statically typed.
Subranges Guard against errors, save space. (only for discrete types)
Array [1..10, 'a'..'z'] of Real = Array [1..10] of Array ['a'..'z'] of RealUser fooled into thinking Array[A,B] of C is AxB->C, but really A->B->C.
Any discrete type as index.
No semi-dynamic arrays. Result of 2 principles:
Type of actual parameters must agree w/ type of formals
Therefore, no general sort routines, etc.
The major problem with Pascal
Procedure x(...; procedure y;...)Fixed in (new) ANSI standard.
:
y(a,2);
No checking if type of file read in matches what was originally written.
2. Problems w/ type compatibility
Assignment compatibility:
When is x := y legal? x : integer, y : 1..10? reverse?
What if type hex = 0..15; ounces = 0..15;
var x : hex; y : ounces;
Is x := y legal?
Original report said both sides must have identical types.
When are types identical?
Ex.:
Type T = Array [1..10] of Integer; Var A, B : Array [1..10] of Integer; C : Array [1..10] of Integer; D : T; E : T;Which variables have the same type?
--> A, B and D, E only.
Structural not always easy. Let
T1 = record a : integer; b : real end; T2 = record c : integer; d : real end; T3 = record b : real; a : integer end;Which are the same?
Worse:
T = record info : integer; next : ^T end; U = record info : integer; next : ^V end; V = record info : integer; next : ^U end;
Ada uses Name EquivalenceA
Pascal & Modula-2 use Name Equivalence for most part. Check!
Modula-3 uses Structural Equivalence
Two types are assignment compatible iff
e.g., type Boolean is (False, True)
Can overload values:
Color is (Red, Blue, Green) Mood is (Happy, Blue, Mellow)If ambiguous can qualify w/ type names:
Color(Blue), Mood(Blue)Subranges Declared w/range attribute.
i.e., Hex is range 0..15
Other attributes available to modify type definitions:
Accurate is digits 20 Money is delta 0.01 range 0.00 .. 1000.00 -- fixed pt!Can extract type attributes:
Hex'FIRST -> 1 Hex'LAST -> 15Can initialize variables in declaration:
declare k : integer := 0
type Two_D is array (1..10, 'a'..'z') of Realor "Unconstrained" (what we called semi-dynamic earlier)
type Real_Vec is array (INTEGER range <>) of REAL;Generalization of open array parameters of MODULA-2.
Of course, to use, must specify bounds,
declare x : Real_Vec (1..10)or, inside procedure:
Procedure sort (Y: in out Real_Vec; N: integer) is -- Y is open array parameter Temp1 : Real_Vec(1..N); -- depends on N Temp2 : Real_Vec (Y'FIRST..Y'LAST); -- depends on parameter Y begin for I in Y'FIRST ..Y'LAST loop ... end loop; ... end sort;Note Ada also has local blocks (like ALGOL 60)
All unconstrained types (w/ parameters) elaborated at block entry (semi-dynamic)
String type is predefined open array of chars:
array (POSITIVE range <>) of character;
Can take slice of 1-dim'l array.
E.g., if
Line : string(1..80)Then can write
Line(10..20) := ('a','b',.'c','d','e','f','g','h','i','j') -- gives assignment to sliceBecause of this structure assignment, can have constant arrays.