Presentation is loading. Please wait.

Presentation is loading. Please wait.

Types and Programming Languages Lecture 10 Simon Gay Department of Computing Science University of Glasgow 2006/07.

Similar presentations


Presentation on theme: "Types and Programming Languages Lecture 10 Simon Gay Department of Computing Science University of Glasgow 2006/07."— Presentation transcript:

1 Types and Programming Languages Lecture 10 Simon Gay Department of Computing Science University of Glasgow 2006/07

2 Types and Programming Languages Lecture 10 - Simon Gay2 Recursion with References f = x.if x==0 then 1 else x*f(x-1) f = let val g = ref ( x:int. x) in g := x:int. if x==0 then 1 else x * (!g)(x-1)); !g end A recursive function definition: can be expressed without explicit recursion: Exercise: check that f(2) behaves correctly.

3 2006/07Types and Programming Languages Lecture 10 - Simon Gay3 Subtyping Imagine BFL with types int and float. If we have f : float  float and v : int then of course we cannot typecheck f(v). But this seems too strict: mathematically we think of the set of integers as a subset of the set of floating point numbers, so we would like to be able to regard v as a float and apply f to it. (At some level of abstraction we want to say that an int is also a float, but note that computation is required to convert the representation of an integer into the representation of the corresponding floating point number, and it might not be possible to do this accurately.)

4 2006/07Types and Programming Languages Lecture 10 - Simon Gay4 Subtyping We can formalize this idea by introducing subtyping. Type T is a subtype of type U, written T <: U, if a value of type T can safely be used anywhere that a value of type U is expected. Conversely we say that U is a supertype of T. It is usually possible to understand T <: U as “every value described by T is also described by U” or “the set of values of T is a subset of the set of values of U”. The crucial rule which brings subtyping into the type system is the rule of subsumption: (T-Sub)

5 2006/07Types and Programming Languages Lecture 10 - Simon Gay5 Defining the subtype relation The subtype relation is defined by a collection of inference rules. General rules: subtyping should be reflexive and transitive. (S-Trans) (S-Refl) Rules for base types are a language design choice. It is common to include natural relationships among numeric types: int <: floatfloat <: double but relationships such as bool <: int or char <: int are probably undesirable.

6 2006/07Types and Programming Languages Lecture 10 - Simon Gay6 Subtyping record types When should a record type be a subtype of another record type? Example: r:{x:int}. #x(r) has type {x:int}  int Giving this function an argument of type {x:int, y:int} should be allowed. For example, ( r:{x:int}. #x(r)){x=1,y=2}  * 1 without any run-time errors. In fact, any record which includes a field x:int is acceptable. In general: (S-RcdWidth) This is called the width subtyping rule for record types.

7 2006/07Types and Programming Languages Lecture 10 - Simon Gay7 Subtyping record types So: one way to create a subtype of a record type is to add more fields. Think: a record type {x:int} describes the set of records with at least a field x of type int. It is also safe to vary the types of individual fields, as long as this variation respects the subtype relation. Example: if sqrt : float  float and f = r:{x:float}. sqrt(#x(r)) then f : {x:float}  float. It is safe to apply f to an expression of type {x:int} because int <: float. So: {x:int} <: {x:float}

8 2006/07Types and Programming Languages Lecture 10 - Simon Gay8 Subtyping record types This gives us the depth subtyping rule for record types: (S-RcdDepth) Examples combining width and depth subtyping.

9 2006/07Types and Programming Languages Lecture 10 - Simon Gay9 Subtyping record types Even if we decided that the order of the fields is significant, we can use subtyping to relax this requirement. (S-RcdPerm) Examples of combinations of the subtyping rules for records.

10 2006/07Types and Programming Languages Lecture 10 - Simon Gay10 Subtyping function types In a language in which functions are values, we must define a subtyping rule for function types. Example: f = x:int  float. sqrt(x(2)) so f : (int  float)  float which types of function can safely be given to f ? If g : int  float then of course f(g) is safe. If g : int  int then is f(g) safe? If g : float  int then is f(g) safe? If g : float  float then is f(g) safe?

11 2006/07Types and Programming Languages Lecture 10 - Simon Gay11 Subtyping function types The rule for subtyping function types is (S-Arrow) The direction of the subtype relation is reversed (contravariant) for the argument types in the left-hand hypothesis, but it is the same (covariant) for the result types as for the function types. It is not safe to say that int  int <: float  float Example: f = x:float  float. sqrt(x(2.3)) so f : (float  float)  float If g : int  int then f(g) evaluates g(2.3) which is an error.

12 2006/07Types and Programming Languages Lecture 10 - Simon Gay12 The Top Type It is convenient to have a type that is a supertype of every type. This type is called Top (think of it as a new base type) and the subtyping rule is T <: Top (S-Top) Any typable expression can be considered to have type Top. Top is similar to the type Object in Java, except that in Java, Object is only a supertype of every object type; for example int is not a subtype of Object.

13 2006/07Types and Programming Languages Lecture 10 - Simon Gay13 Objects Following Pierce (Chapter 18) we will look at how the language features we have been studying can be used to construct objects with behaviour familiar from OO languages. What is an object? A data structure encapsulating some internal state and providing a collection of methods which allow access to this state. Example: a counter object, holding an integer value and providing get and inc methods.

14 2006/07Types and Programming Languages Lecture 10 - Simon Gay14 A Counter Object Let’s give the name c to an expression representing a counter. c = let val x = ref 1 in { get = _:unit. !x, inc = _:unit. x := (!x)+1 } end The type of c is { get: unit -> int, inc: unit -> unit } The scoping rules mean that x is private, but can be used by both get and inc.

15 2006/07Types and Programming Languages Lecture 10 - Simon Gay15 Using the counter To call a method we extract a field of the record and apply it. c.inc() c.get() (Change notation: write c.inc instead of #inc(c) ) returns () returns 2 What we are really doing: let val c = let val x = ref 1 in {... } end in c.inc(); c.get() end

16 2006/07Types and Programming Languages Lecture 10 - Simon Gay16 Using the counter We can define an abbreviation for the type of the counter: Counter = { get: unit -> int, inc: unit -> unit } A function which uses a counter object: inc3 = x:Counter.(x.inc(); x.inc(); x.inc()) Example: Reduction sequences for expressions involving counters.

17 2006/07Types and Programming Languages Lecture 10 - Simon Gay17 Subtyping object types Extending the object type by adding a new method: Counter = { get: unit -> int, inc: unit -> unit } ResetCounter = { get: unit -> int, inc: unit -> unit, reset: unit -> unit } According to the subtyping rules, ResetCounter <: Counter. This is what we would expect in an OO language (e.g. Java) if we define class ResetCounter extends Counter.

18 2006/07Types and Programming Languages Lecture 10 - Simon Gay18 Method overriding When a subclass is defined in Java, it is possible for a method to be overridden by a method of the same name and type. class Counter { float x; float get() { return x; } void inc() { x = x + 1; } } class NewCounter extends Counter { float get() { return x+1; } } This does not change the corresponding record type: { get: unit -> float, inc: unit -> unit }

19 2006/07Types and Programming Languages Lecture 10 - Simon Gay19 Method overriding Using the flexibility of record subtyping, it would be safe to allow variation of method types when overriding, e.g.: class IntCounter extends Counter { int y; int get() { return y; } void inc() { y = y + 1; } } { get: unit -> int, inc: unit -> unit } The type of IntCounter as a record is which is a subtype of { get: unit -> float, inc: unit -> unit } Contravariant modification of method parameter types is also safe.

20 2006/07Types and Programming Languages Lecture 10 - Simon Gay20 References and subtyping The Ref type constructor must be invariant to preserve safety. (S-Ref) For Ref S to be a subtype of Ref T, S and T must each be a subtype of the other – they must be equivalent in the subtype relation. This means that either S and T are identical, or they are record types with the same fields in a different order. Why is such a severe restriction necessary?

21 2006/07Types and Programming Languages Lecture 10 - Simon Gay21 References and subtyping A value of type Ref T can be used in two different ways in a given context: for reading (!) or for writing (:=). When used for reading, the context expects to obtain a value of type T, so if the reference actually holds a value of type S then we must have S <: T. If the reference is used for writing then the context will write a value of type T. If the actual type of the reference is Ref S, then at some other point the value will be used as if it had type S; this is only safe if T <: S.

22 2006/07Types and Programming Languages Lecture 10 - Simon Gay22 Arrays and subtyping An array is a collection of references of the same type, so the subtyping rule is invariant in the same way as the rule for references. (S-Array) Java permits covariant subtyping of arrays: (S-ArrayJava) This is unsafe and requires every array assignment to have a run-time type check!

23 2006/07Types and Programming Languages Lecture 10 - Simon Gay23 Unsafe array subtyping in Java Why does Java provide this unsafe subtyping rule for arrays? To allow flexibility of programming in situations such as void reverse(Object[] a) so that an array of any type can be given to reverse as a parameter. Int[] a = new Int[100];... reverse(a); But this comes at a very high cost, and better solutions are possible through parametric polymorphism (as we will see later).

24 2006/07Types and Programming Languages Lecture 10 - Simon Gay24 Arrays and Subtyping in Java void f(Object[] x) { x[0] = new Object(); } String[] a = new String[10]; for (int i = 0; i < 10; i++) a[i] = “Test”; f(a); System.out.println (a[0].concat(“A”));

25 2006/07Types and Programming Languages Lecture 10 - Simon Gay25 Casts Java (and other languages) provide casting which forces a value of one type to be regarded as having another type. Upcasts: if S <: T and e:S then writing (T)e is harmless and just has the effect of losing information about e. Example: if we have c:ResetCounter and we write d = (Counter)c then referring to d does not allow us to use the reset method. Downcasts: if S <: T and e:T then writing (S)e requires a run-time check that e actually does have type S. For example, e might be the result of upcasting a value of type S.

26 2006/07Types and Programming Languages Lecture 10 - Simon Gay26 Downcasting A very common use of downcasting is when using generic data structures. Example (in Java before 1.5): if we want to use a Hashtable then usually we are storing a specific type of object, but all the methods of the Hashtable class have types involving Object in order to make them generic. Hashtable h = new Hashtable(); MyClass m = new MyClass(...); h.put(“Simon”,m);... MyClass n = (MyClass)h.get(“Simon”); because Object get(Object key) in Hashtable. Again, the right solution is parametric polymorphism (see later).

27 2006/07Types and Programming Languages Lecture 10 - Simon Gay27 Reading Pierce: 15.1, 15.2, 15.3, 15.4, 15.5, 18.1-18.7. Exercises Pierce: 15.2.1, 15.2.5, 15.5.2, 15.5.3.


Download ppt "Types and Programming Languages Lecture 10 Simon Gay Department of Computing Science University of Glasgow 2006/07."

Similar presentations


Ads by Google