CS 334 Lecture 15

# More Polymorphic Type Systems

Last time did implicit polymorphism

Explicit polymorphism: Clu, Ada, Eiffel, Modula-3, C++

No type inference. If have polymorphic function, must explicitly pass type parameter.

E.g., fun map t u (f: t -> u) (l: t list): u list

Apply: map string int length ["a","help","Willy"] = [1,4,5]

Write type as

forall t. forall u. (t -> u) -> (t list) -> u list

Makes clear that t, u are type variables.

Can understand implicit polymorphic terms as abbreviations for explicit polymorphic terms. Compare with type of map in ML, above.

Explicit polymorphic terms more expressive!

Restrictions on polymorphism for ML.

Polymorphic functions can be defined at top-level or in let clauses, but polymorphic function expressions cannot be used as arguments of functions.

E.g.,

```	let fun id x = x in (id "ab", id 17) end;
```
is fine, but can't write
```	let fun test g = (g [], g "ab")
in test (fn x => x) end;
```
In fact, can't even write:
```	fun test2 f = (f [], f 17);
```
Gets confused since f is used with two different typings:

'a list -> 'b, int -> 'c and can't unify 'a list and int.

No problem writing this in explicit polymorphic language:

```    let fun test  (g: forall t.t -> t): (int list, string) =
(g (int list) [], g string "ab")
in test (Fn t => fn (x:t) => x) end;
```

Write f: forall t. T, if f takes a type parameter U and returns a value of type T[U/t].
Use Fn t => to indicate parameter is a type.

Thus "id", defined by:

```	fun id t (x:t) = x
```
has type: forall t. t -> t.

A polymorphic function is one which takes a type parameter, which specializes it into a function of particular type. I.e., id T is function of type T -> T.

Think of "id" as representing a class of functions, each of which has uniform (parameterized) type.

Subtypes

When can an element of one type be used in a context which expects another?

If an element of type T can be used whereever an element of type T' is expected then say T is a subtype of T' (write T <= T').

Example: Integer <= Real, 1..10 <= Integer

Note this is only true of expressions, not variables!

Can understand definition of subtype as implying existence of a (well-behaved) implicit coercion from sub to supertype.

Well-behaved = homomorphism w.r.t. operators. I.e., ToReal: Integer -> Real

```	ToReal(2 + 3 * 4) = ToReal(2) + ToReal(3) * ToReal(4)
```
Ada provides subranges of all primitive types (including reals): always inherit operations of base class.

Get subtypes of open array type by freezing bounds.

Get subtype of variant record by freezing variant.

Are there other reasonable subtypes?

Suppose have

```	type point = {x, y: real};
datatype colortype = Red | Blue | Yellow | Green;
type colorpoint = {x,y : real; color: colortype}
```

Is colorpoint <= point?

Suppose have

```	fun dist(p:point):real = sqrt(sqr(p.x) + sqr(p.y));
```
Is there any reason this cannot be applied to cp:colorpoint?

Suppose have

```   fun move(p:point,dx:real,dy:real) : point = {x = p.x + dx, y = p.y + dy}
```

Note that move(cp,1.2,3.6) returns a point rather than a colorpoint.

If want a colorpoint, then must make up new notation:

```	fun move(p,dx,dy) = p gets {x = p.x + dx, y = p.y + dy}
```

"Gets" updates particular components of a record. Thus cp gets {x=1.2} results in a colorpoint whose x component is 1.2

Need a more expressive type system to capture functionality of this term. Easiest in a language with explicit parametric polymorphism. Gives "bounded" polymorphism.

Write

```    fun move(t <= point, p : t, dx : real,dy : real): t =
p gets {x = p.x + dx, y = p.y + dy}
```
Type is
```	forall t <= point. t * real * real -> t
```

Decided that for

```	type point = {x, y: real};
datatype colortype = Red | Blue | Yellow | Green;
type colorpoint = {x,y : real; color: colortype}
```

Get Colorpoint <= point

But Colorpoint is not a subset of Point!!

Set of triples not subset of set of pairs.

Two ways of looking at it.

1. Interpret

Point = {p | p is a record with (at least) x and y components which are reals}

Colorpoint = {p | p is a record with (at least) x and y components which are reals, and color component which is colortype}.

Then Colorpoint is a subset of Point.

Fits intuition of subtypes as subsets, but there are technical problems.

If p, q are elts of Point, then how do we determine if p = q?
Is it enough to have p.x = q.x and p.y = q.y?

If so, then green point at origin is equal to red point at origin!

Seem equal as points, but different as colorpoints!

2. Give up intuition of subtypes as subtypes and go back to intuition with implicit coercions.

Clear implicit coercion from colorpoint to point - simply forget color component.

Behaves nicely with respect to operations on records.

Can define elements of type point as equivalence classes of records from set point from 1.

Define p =pt q iff p.x = q.x and p.y = q.y

Define p =cpt q iff p.x = q.x, p.y = q.y, and p.color = q.color.

Therefore can have p, q elements of colorpoint s.t. p =pt q but not p =cpt q.

Model built from this given in Bruce & Longo, 1990.

Slightly different model based on definable coercions given by Breazu-Tannen et al in 1991.

Both provide models of language with subtypes and explicit parametric polymorphism.

# Object-oriented programming languages

## Roots in languages supporting ADT's

Biggest loss in moving from FORTRAN to Pascal is lack of support for modules with persistent local data.

Clu, Ada, and Modula 2 attempted to remedy this by adding clusters, packages, and modules.

In Ada & Modula 2, objects (i.e. packages, and modules) were late additions to an earlier paradigm (Pascal-like)

• couldn't be instantiated dynamically

• had no type or other method for organizing, despite similarity to records.

• nonetheless provide better modules for building large systems
Called object-based languages.

## Goals:

Stepping back a bit further - support development of high-quality software.

### Qualities Desired in Software

• Correctness: Should be able to guarantee correctness of code on legal input

• Robustness: Should work in unusual cases, handle errors

• Extensibility: Should be able to add features

• Reusability: Should be able to use code in different circumstances

• Compatibility: Should be able to combine modules

ADT languages provide reasonable support for all but extensibility (in particular if want minor extensions - but rest is same), some limitations on reuseability.

Object-oriented languages are an attempt to make progress toward these goals.

A programming language is object-oriented if:

1. It supports objects that are data abstractions (like Modula 2, Ada).

2. All objects have an associated object type (often called classes). (Bit different from Modula 2, Ada).

3. Classes may inherit attributes from superclasses. (Very different from Modula 2, Ada)

4. Computations proceed by sending messages to objects.

5. Routines may be applied to objects which are variants of those they are designed to be applied to (subtype polymorphism).

6. Support dynamic method invocation (will be explained later )

Simula 67 first object-oriented language - designed for discrete simulations.

Up until recently, Smalltalk best-known - Alan Kay at Xerox (now at Apple).

Gone through Smalltalk-72,-74,-76,-78,-80.

C++, object-oriented extensions to Pascal, C, LISP, etc.

One of nicest is Eiffel - discuss later (See Meyer's Object-Oriented Software Construction). Also Sather (public-domain variant of Eiffel).

### Main idea of object-oriented programming:

Independent objects cooperate to perform a computation by sending messages (requests for action) to each other.

## Object-oriented programming:

Object-oriented languages built around following concepts:

• Object - like "internal representation of abstract data types - all data encapsulated in objects - first class!

• Message - request for object to carry out one of its operations.

• Class - template for set of objects - similar to type

• Instance - object described by a class

• Method - operation associated with an object - specified in its class.

• Subtype - A subtype of B (A <= B) if A represents a specialization of B (e.g., cars <= vehicles, ColorPoint <= Point) - An element of a subtype can be used in any context in which an element of the supertype would work.

• Subclass - An incremental modification or extension of a class and its methods. Methods not changed are inherited.

Objects are internal data abstractions - only accessible to outer world through associated procedures

• hide representation from outer world

• have an associated state (current contents of internal variables)

• its methods have automatic access to its state, therefore no explicit parameter needed for self.

Object types

• allow objects (modules) to be first-class citizens

• allow use in assignment, parameters, components of structures

• allow objects to be classified via subtyping.

Classes

• templates for the creation of objects

• contain definitions of all methods associated with these objects

• can modify or extend by creating subclasses which inherits old methods

Most current OOL's identify object types and classes (in particular subtypes and subclasses).

See later this can lead to holes in typing system and/or restrictions in expressibility.

In typical object-oriented programming language, everything is an object.

Abstraction preserved since no object can make changes to the internal state of another object. (Some languages don't enforce.)

- just send messages using methods in public interface.

## Eiffel

We will be using the OO language, EIFFEL3.

It is one of best on the market, though it has some important flaws.

To use Eiffel, type:

```	source EiffelSetup
ebench
```
You can follow ebench with "&" if wish to spawn it off as a separate process, but then programs can't get keyboard input when running in the debug system.

If have problems starting up ebench, try typing:

```	echo \$EIFFEL3 \$PLATFORM
```
to make sure that EiffelSetup worked properly.

Let's start with a very simple example consisting of 3 classes. The following should be stored in a file, point.e.

All sample programs can be found in ~kim/cs334stuff/Eiffel

```class POINT
-- class which suports a movable point

creation    -- designates a method which may be used when
-- creating a POINT object.
Create
-- Note:  When an object is defined it sets all attributes to
--  default value for that type (e.g. 0 for integer and
--  real).  Can also designate one or more features which can
--  be called to do further initializations

feature

-- Create point at origin and push it onto `lp'
require
lp /= Void
do
lp.put (Current)  -- Current is name for "self", the                                    -- object executing the method.
end; -- Create

x, y: REAL;

translate (a, b: REAL) is
-- Move by `a' horizontally, `b' vertically.
do
x := x+a;
y := y+b
end; -- translate

scale (factor: REAL) is
-- Scale by a ratio of `factor'.
do
x := factor * x;
y := factor * y
end; -- scale

display is
-- Output position of point
do
io.putstring ("Current position: x = ");
io.putreal (x);
io.putstring ("; y = ");
io.putreal (y);
io.new_line -- writeln
end -- display

end -- class POINT
```
Note that "io" is a feature of every class (automatically inherited from ANY) which provides io features. Simply send messages to it to do I/O

In a separate file: interaction.e:

```class INTERACTION
-- simple program demonstrating creation and handling of
-- requests in Eiffel

creation
create  -- while name is the same as Point, it need not be!

feature {NONE}  -- NONE means these are inaccessible outside of INTERACTION

my_point: POINT;

request: INTEGER;

Up, Down, Left, Right, Quit: INTEGER is unique;
-- equivalent of user-defined type

point_stack: LINKED_STACK [POINT];  -- from library

feature -- since no qualifier, these are public

over: BOOLEAN;

Create is
-- Create a point
do
!!point_stack.make; -- create and execute "make".
!!my_point.Create (point_stack);
end; -- Create

get_request is
-- Ask what the user wants to do next,
-- returning the answer in attribute `request':
-- `Up', `Down', `Left', `Right' or `Quit'.
local
correct: BOOLEAN
do
-- all loops are of from .. until .. loop form
from        -- anything after from is initialization
-- correct := false <- automatically set!
until   -- continue until the following cond'n true!
correct
loop
io.new_line;
io.putstring ("Enter command (one character)");
io.new_line;
io.putstring ("U for Up, D for Down, L for Left, %
%R for Right, Q for Quit: ");
-- % indicates continue string to next line
io.next_line;
correct := true;

inspect -- inspect is like a case statement.
when 'u', 'U' then
request := Up
when 'd', 'D' then
request := Down
when 'l', 'L' then
request := Left
when 'r', 'R' then
request := Right
when 'q', 'Q' then
request := Quit
else
io.new_line;
io.new_line;
correct := false
end
end
end; -- get_request

one_command is
-- Get user request and execute it
do
get_request;
inspect request
when Up then
my_point.translate (0., 1.)
when Down then
my_point.translate (0., -1.)
when Left then
my_point.translate (-1., 0.)
when Right then
my_point.translate (1., 0.)
when Quit then
over := true
end;
my_point.display
end -- one_command

end -- class INTERACTION
```
Finally, the class which is executed as the main program:

```class SESSION

creation
Create

feature

Create is
-- Execute sequence of interactive commands
local
interface: INTERACTION
do
from
!!interface.Create
until
interface.over
loop
interface.one_command
end
end -- Create

end -- class SESSION
```

This is all controlled by the following "ACE"

```system
TRY_EIFFEL
-- Replace SYSTEM_NAME by the name of the executable file
-- to be generated for your system.

root

SESSION (ROOT_CLUSTER): "create"
-- Replace ROOT_CLASS, ROOT_CLUSTER and creation_procedure
-- by the names of the root class, root class cluster and
-- root creation procedure for your system.
-- The `(ROOT_CLUSTER)' part may be omitted if there is
-- no other class of name ROOT_CLASS in the universe.

default

assertion (require);
precompiled ("\$EIFFEL3/precompiled/spec/\$PLATFORM/base")

cluster

ROOT_CLUSTER:   ".";
-- Replace ROOT_CLUSTER and PATH by the names of the
-- root class cluster & path for your system.

kernel:             "\$EIFFEL3/library/base/kernel";
support:            "\$EIFFEL3/library/base/support";
access:             "\$EIFFEL3/library/base/structures/access";
cursors:            "\$EIFFEL3/library/base/structures/cursors";
cursor_tree:        "\$EIFFEL3/library/base/structures/cursor_tree";
dispenser:          "\$EIFFEL3/library/base/structures/dispenser";
iteration:          "\$EIFFEL3/library/base/structures/iteration";
list:               "\$EIFFEL3/library/base/structures/list";
obsolete:           "\$EIFFEL3/library/base/structures/obsolete";
set:                "\$EIFFEL3/library/base/structures/set";
sort:               "\$EIFFEL3/library/base/structures/sort";
storage:            "\$EIFFEL3/library/base/structures/storage";
table:              "\$EIFFEL3/library/base/structures/table";
traversing:         "\$EIFFEL3/library/base/structures/traversing";
tree:               "\$EIFFEL3/library/base/structures/tree";

end
```
Here is another example from Eiffel using generics to support ordered pairs and rationals:

```deferred class ORDERED_PAIR2 [T]
-- this class cannot be instantiated because method display
-- is deferred!
feature
x : T;  -- first coordinate
y : T;  -- second coordinate

setx(r : T) is
-- set first coordinate
do
x := r
end; -- setx

sety(r : T) is
-- set second coordinate
do
y := r
end; -- sety

display is
-- display the ordered pair
deferred    -- must be filled in in subclass
end; -- display

same(other : like Current) : BOOLEAN is
do
Result := (x = other.x) and (y = other.y)
end -- same
end -- ORDERED_PAIR2
```

Return answer from function by assigning to "Result".

When use must instantiate T, see NEWRATIONAL below. Must also instantiate any deferred features (e.g., display) before can use.

Subclasses and Inheritance

A new class can be declared to be a subclass of any other class. The new class then "inherits" all features of the old class (think of this as almost like copying the code for all features of old class into the new class).

The new class can add new features or redefine old ones.

```class NEWRATIONAL

inherit
ORDERED_PAIR2 [INTEGER]
rename  x as n, -- can change names of features
y as d
redefine same   -- indicates that same will be redefined.
-- Need not mention display since it was deferred!
end

creation Create

feature

Create is
-- create a rational
do
d := 1
end; -- Create

feature {NONE}  -- private method
reduce : INTEGER is
-- reduce to lowest terms
local
num,den,next : INTEGER
do
if (n =0) or (d = 0) then
Result := 1
else
if n < 0 then num := -n else num := n end;
if d < 0 then den := -d else den := d end;

from
next := num \\ den  -- \\ is mod operator
invariant   -- must be true each time through loop
((num \\ next) = 0) and ((den \\ next) = 0)
variant -- must decrease each time through loop
next
until
next = 0
loop
num := den;
den := next;
next := (num \\ den)
end;

Result := den
end
end; -- reduce

feature

set(numer, denom : INTEGER) is
-- set the numerator and denominator
-- post: d > 0
require -- precondition
denom /= 0
local
gcd : INTEGER
do

n := numer;
d := denom;

if d < 0 then
n := -n;
d := -d
end;

gcd := reduce;

n := n // gcd;
d := d // gcd
ensure  -- postcondition
d > 0
end; -- set

-- get rational in form n/d from input
local
num, den, attempts : INTEGER
do
num := io.lastint;
den := io.lastint;
set(num,den)
ensure
d > 0
rescue  -- exception handler
if attempts < 3 then
io.next_line;   -- go to next input line
io.new_line;    -- go to next output line
io.putstring("A fraction is an integer ");
io.putstring("divided by a non-zero integer.");
io.putstring("  Enter a fraction: ");
attempts := attempts + 1;
retry
end

display is
-- display the fraction
do
if n = d*(n // d) then
io.putint(n // d)
else
io.putint(n);
io.putchar('/');
io.putint(d)
end
end; -- display

same(other : like Current) : BOOLEAN is
-- are the fractions equal?
do
Result := (n*other.d = d*other.n)
end; -- same

lessthan(other : like Current) : BOOLEAN is
-- is Current < other
do
Result := (n*other.d < d*other.n)
end;

invariant
d /= 0

end -- NEWRATIONAL
```
Note that "like Current" in "lessthan" denotes the class of the object receiving the message.

Can also use "like x" for x any instance variable of class.

Declaring class to be "like Current" helps ensure that routine will work properly in subclasses - guarantees class of argument same as class of object sending message to.

Can also have multiple inheritance (new class inherits from more than one class).

Can add capabilities to any NEWRATIONAL with a subclass:

```class RATIONALMATH

inherit
NEWRATIONAL

creation
Create

feature
plus(other : like Current) : like Current is
local
sumnum, sumden : INTEGER;
do
sumnum := n*other.d + other.n*d;
sumden := d*other.d;
!!Result.Create;
Result.set(sumnum,sumden)
end; -- plus

end -- RATIONALMATH
```

A main program which uses RATIONALMATHis simply a class whose create procedure is the routine which is executed by the system.

```class TESTRATIONAL

creation
Create
feature

Create is
-- manipulate some rational numbers
local
p1,p2,p3 : RATIONALMATH
do
!!p1.Create;
!!p2.Create;
!!p3.Create;
io.putstring("Enter a fraction as n/d: ");
io.putstring("Enter a fraction as n/d: ");
p1.display;
io.new_line;
p2.display;
io.new_line;

if p1.same(p2) then
io.putstring("They're equal")
else
io.putstring("They're not equal")
end;

io.new_line;

end -- Create

end -- TESTRATIONAL
```

Note that all variables should be thought of as references to objects.

Can't do anything except assign to a variable until you create it! (Illegal to send a message to an uninitialized variable.)

Thus a := b means that a now refers to the same object as b (sharing).

Important:: Note that only local variables or own attributes may be assigned to or created

• never create or assign to formal parameters! Can only send messages to parameters.

• Essentially call-by-constant value.

• Can't change reference, but can ask parameter to change what it refers to!

To give "a" a new copy of object referred to by "b", write a := clone(b)
Note that you need not have created a before you can do this!

"a = b" is true iff a and b refer to the same object.

"equal(a,b)" is true iff the fields of a and b are identical.

Note that clone and equal are routines available in any class.
They are defined in class ANY which has features which are available to all other classes. ANY also includes io which was used earlier.