71
TI1220 2012-2013 Concepts of Programming Languages Eelco Visser / TU Delft Lecture 8: Traits & Type Parameterization

TI1220 Lecture 8: Traits & Type Parameterization

Embed Size (px)

Citation preview

Page 1: TI1220 Lecture 8: Traits & Type Parameterization

TI1220 2012-2013Concepts of Programming Languages

Eelco Visser / TU Delft

Lecture 8: Traits & Type Parameterization

Page 2: TI1220 Lecture 8: Traits & Type Parameterization

var ms := course.managers;

var ms : Set<Person> := Set<Person>();ms.addAll(course.managers);

Analysis => Lecture 12: Concurrency

Root Cause

The Fix

Intention: copy semantics

Effect: reference semantics

The Fault: Concurrent writes on CourseEdition

Page 3: TI1220 Lecture 8: Traits & Type Parameterization

The Airconditioning

Page 5: TI1220 Lecture 8: Traits & Type Parameterization

Traits

Page 6: TI1220 Lecture 8: Traits & Type Parameterization

abstract class Element { def contents: Array[String] def height: Int = contents.length def width: Int = if (height == 0) 0 else contents(0).length}

class ArrayElement(conts: Array[String]) extends Element { val contents: Array[String] = conts}

Classical Inheritance

Inheriting fields and methods

Page 7: TI1220 Lecture 8: Traits & Type Parameterization

class UniformElement( ch: Char, override val width: Int, override val height: Int) extends Element { private val line = ch.toString * width def contents = Array.make(height, line)}

val e1: Element = new ArrayElement(Array("hello", "world"))val ae: ArrayElement = new LineElement("hello")val e2: Element = aeval e3: Element = new UniformElement('x', 2, 3)

Subtyping

Page 8: TI1220 Lecture 8: Traits & Type Parameterization

abstract class Element { def demo() { println("Element's implementation invoked") }}class ArrayElement extends Element { override def demo() { println("ArrayElement's implementation invoked") }}class LineElement extends ArrayElement { override def demo() { println("LineElement's implementation invoked") }}// UniformElement inherits Element’s democlass UniformElement extends Element

def invokeDemo(e: Element) { e.demo()}scala> invokeDemo(new ArrayElement)ArrayElement's implementation invoked

scala> invokeDemo(new LineElement)LineElement's implementation invoked

scala> invokeDemo(new UniformElement)Element's implementation invoked

Dynamic Binding

Page 9: TI1220 Lecture 8: Traits & Type Parameterization

Subtyping

• Polymorphism & dynamic binding

Code Reuse

• reuse instance variables and methods from super class

Single Inheritance

• cannot reuse code from more than one class

Interfaces

• support subtyping multiple classes

• must re-implement interface

Java-style Single Inheritance

Page 10: TI1220 Lecture 8: Traits & Type Parameterization

Trait

• reusable unit of code

• encapsulates method and field definitions

• reused by mixing into classes

• class can mix in any number of traits

Applications

• rich interfaces

• stackable modifications

Page 11: TI1220 Lecture 8: Traits & Type Parameterization

trait Philosophical { def philosophize() { println("I consume memory, therefore I am!") }}class Frog extends Philosophical { override def toString = "green"}

scala> val frog = new Frogfrog: Frog = greenscala> frog.philosophize()I consume memory, therefore I am!

scala> val phil: Philosophical = frogphil: Philosophical = greenscala> phil.philosophize()I consume memory, therefore I am!

inheritance: code reuse subtyping: traits are types

Defining and Using Traits

Page 12: TI1220 Lecture 8: Traits & Type Parameterization

class Animaltrait HasLegsclass Frog extends Animal with Philosophical with HasLegs { override def toString = "green" override def philosophize() { println("It ain't easy being " + toString + "!") }}

superclasstraits

override code from traitscala> val phrog: Philosophical = new Frogphrog: Philosophical = greenscala> phrog.philosophize()It ain't easy being green!

Mixing in (multiple) traits

Page 13: TI1220 Lecture 8: Traits & Type Parameterization

Trait is like Java interface with

• methods

• fields

• state

Trait is Scala class

• without class parameters

• dynamic binding of ‘super’

Page 14: TI1220 Lecture 8: Traits & Type Parameterization

Rich interface

• many methods

• convenient for client

• more work for implementer

Thin interface

• few methods

• easy for implementers

• inconvenient for client

trait CharSequence { def charAt(index: Int): Char def length: Int def subSequence(start: Int, end: Int): CharSequence def toString(): String}

Rich Interfaces with Traits

• small number of abstract methods implemented by client

• large number of concrete methods inherited by client

Page 15: TI1220 Lecture 8: Traits & Type Parameterization

class Point(val x: Int, val y: Int)class Rectangle(val topLeft: Point, val bottomRight: Point) { def left = topLeft.x def right = bottomRight.x def width = right - left // and many more geometric methods...}

abstract class Component { def topLeft: Point def bottomRight: Point def left = topLeft.x def right = bottomRight.x def width = right - left // and many more geometric methods...}

Rectangular objects without traits

Page 16: TI1220 Lecture 8: Traits & Type Parameterization

trait Rectangular { def topLeft: Point def bottomRight: Point def left = topLeft.x def right = bottomRight.x def width = right - left // and many more geometric methods...}

abstract class Component extends Rectangular { // other methods...}

class Rectangle(val topLeft: Point, val bottomRight: Point) extends Rectangular { // other methods...}

Rectangular objects with traits

Page 17: TI1220 Lecture 8: Traits & Type Parameterization

trait Rectangular { def topLeft: Point def bottomRight: Point def left = topLeft.x def right = bottomRight.x def width = right - left // and many more geometric methods...}

abstract class Component extends Rectangular { // other methods...}

class Rectangle(val topLeft: Point, val bottomRight: Point) extends Rectangular { // other methods...}

scala> val rect = new Rectangle(new Point(1, 1), new Point(10, 10))rect: Rectangle = Rectangle@3536fdscala> rect.leftres2: Int = 1scala> rect.rightres3: Int = 10

Rectangular objects with traits

Page 18: TI1220 Lecture 8: Traits & Type Parameterization

class Rational(n: Int, d: Int) { // ... def <(that: Rational) = this.numer * that.denom > that.numer * this.denom def >(that: Rational) = that < this def <=(that: Rational) = (this < that) || (this == that) def >=(that: Rational) = (this > that) || (this == that)}

defined in terms of < based on standard

semantics of ordering

{

A Rich Interface for Ordering

Page 19: TI1220 Lecture 8: Traits & Type Parameterization

class Rational(n: Int, d: Int) { // ... def <(that: Rational) = this.numer * that.denom > that.numer * this.denom def >(that: Rational) = that < this def <=(that: Rational) = (this < that) || (this == that) def >=(that: Rational) = (this > that) || (this == that)}

defined in terms of < based on standard

semantics of ordering

{

class Rational(n: Int, d: Int) extends Ordered[Rational] { // ... def compare(that: Rational) = (this.numer * that.denom) - (that.numer * this.denom)}

A Rich Interface for Ordering

Page 20: TI1220 Lecture 8: Traits & Type Parameterization

class Rational(n: Int, d: Int) { // ... def <(that: Rational) = this.numer * that.denom > that.numer * this.denom def >(that: Rational) = that < this def <=(that: Rational) = (this < that) || (this == that) def >=(that: Rational) = (this > that) || (this == that)}

defined in terms of < based on standard

semantics of ordering

{

class Rational(n: Int, d: Int) extends Ordered[Rational] { // ... def compare(that: Rational) = (this.numer * that.denom) - (that.numer * this.denom)}

Ordered trait provides reusable implementation of ordering

A Rich Interface for Ordering

Page 21: TI1220 Lecture 8: Traits & Type Parameterization

Class Queue of integers

• put: place integer in queue

• get: take integer out

• first-in first-out

Modifications

• Doubling: double all integers put in queue

• Incrementing: increment all integers put in queue

• Filtering: filter out negative

Stackable Modifications

Page 22: TI1220 Lecture 8: Traits & Type Parameterization

abstract class IntQueue { def get(): Int def put(x: Int)}

import scala.collection.mutable.ArrayBufferclass BasicIntQueue extends IntQueue { private val buf = new ArrayBuffer[Int] def get() = buf.remove(0) def put(x: Int) { buf += x }}

Class Queue

Page 23: TI1220 Lecture 8: Traits & Type Parameterization

abstract class IntQueue { def get(): Int def put(x: Int)}

import scala.collection.mutable.ArrayBufferclass BasicIntQueue extends IntQueue { private val buf = new ArrayBuffer[Int] def get() = buf.remove(0) def put(x: Int) { buf += x }}

scala> val queue = new BasicIntQueuequeue: BasicIntQueue = BasicIntQueue@24655f

scala> queue.put(10)

scala> queue.put(20)

scala> queue.get()res9: Int = 10

scala> queue.get()res10: Int = 20

Class Queue

Page 24: TI1220 Lecture 8: Traits & Type Parameterization

trait Doubling extends IntQueue { abstract override def put(x: Int) { super.put(2 * x) }}

dynamically bound

can only be mixed into subclasses of IntQueue

mix into class with concrete

definition

Trait Doubling

Page 25: TI1220 Lecture 8: Traits & Type Parameterization

trait Doubling extends IntQueue { abstract override def put(x: Int) { super.put(2 * x) }}

dynamically bound

can only be mixed into subclasses of IntQueue

mix into class with concrete

definition

scala> class MyQueue extends BasicIntQueue with Doublingdefined class MyQueue

scala> val queue = new MyQueuequeue: MyQueue = MyQueue@91f017

scala> queue.put(10)

scala> queue.get()res12: Int = 20

Trait Doubling

Page 26: TI1220 Lecture 8: Traits & Type Parameterization

trait Doubling extends IntQueue { abstract override def put(x: Int) { super.put(2 * x) }}

dynamically bound

can only be mixed into subclasses of IntQueue

mix into class with concrete

definition

scala> class MyQueue extends BasicIntQueue with Doublingdefined class MyQueue

scala> val queue = new MyQueuequeue: MyQueue = MyQueue@91f017

scala> queue.put(10)

scala> queue.get()res12: Int = 20

scala> val queue = new BasicIntQueue with Doublingqueue: BasicIntQueue with Doubling = $anon$1@5fa12d

scala> queue.put(10)

scala> queue.get()res14: Int = 20

Trait Doubling

Page 27: TI1220 Lecture 8: Traits & Type Parameterization

trait Incrementing extends IntQueue { abstract override def put(x: Int) { super.put(x + 1) }}trait Filtering extends IntQueue { abstract override def put(x: Int) { if (x >= 0) super.put(x) }}

Stacking Modifications

Page 28: TI1220 Lecture 8: Traits & Type Parameterization

trait Incrementing extends IntQueue { abstract override def put(x: Int) { super.put(x + 1) }}trait Filtering extends IntQueue { abstract override def put(x: Int) { if (x >= 0) super.put(x) }}

scala> val queue = (new BasicIntQueue with Incrementing with Filtering)scala> queue.put(-1); queue.put(0); queue.put(1)

scala> queue.get()res15: Int = 1scala> queue.get()res15: Int = 2

Stacking Modifications

Page 29: TI1220 Lecture 8: Traits & Type Parameterization

trait Incrementing extends IntQueue { abstract override def put(x: Int) { super.put(x + 1) }}trait Filtering extends IntQueue { abstract override def put(x: Int) { if (x >= 0) super.put(x) }}

scala> val queue = (new BasicIntQueue with Incrementing with Filtering)scala> queue.put(-1); queue.put(0); queue.put(1)

scala> queue.get()res15: Int = 1scala> queue.get()res15: Int = 2

Stacking Modifications

scala> val queue = (new BasicIntQueue with Filtering with Incrementing)scala> queue.put(-1); queue.put(0); queue.put(1)

scala> queue.get()res17: Int = 0scala> queue.get()res18: Int = 1scala> queue.get()res19: Int = 2

Page 30: TI1220 Lecture 8: Traits & Type Parameterization

// Multiple inheritance thought experimentval q = new BasicIntQueue with Incrementing with Doublingq.put(42) // which put would be called?

Multiple Inheritance (Why Not?)

Incrementing Doubling

BasicIntQueue

new BasicIntQueue with Increment with Doubling

Page 31: TI1220 Lecture 8: Traits & Type Parameterization

// Multiple inheritance thought experimenttrait MyQueue extends BasicIntQueue with Incrementing with Doubling { def put(x: Int) { Incrementing.super.put(x) // (Not real Scala) Doubling.super.put(x) }}

Multiple Inheritance (Why Not?)

Incrementing Doubling

BasicIntQueue

new BasicIntQueue with Increment with Doubling

put of BasicIntQue called twice!

Page 32: TI1220 Lecture 8: Traits & Type Parameterization

a class is always linearized before all of its superclasses and mixed in traits

class Animaltrait Furry extends Animaltrait HasLegs extends Animaltrait FourLegged extends HasLegsclass Cat extends Animal with Furry with FourLegged

Linearly Ordering Traits

Page 33: TI1220 Lecture 8: Traits & Type Parameterization

Units of code

• reusable through inheritance

• can be mixed in at multiple places in hierarchy

Multiple inheritance ++

• calls to super are linearized

• avoid diamond problem

• stack changes

Traits Summary

Page 34: TI1220 Lecture 8: Traits & Type Parameterization

What is the value of question in:

class Animal { override def toString = "Animal"}trait Furry extends Animal { override def toString = "Furry -> " + super.toString}trait HasLegs extends Animal { override def toString = "HasLegs -> " + super.toString}trait FourLegged extends HasLegs { override def toString = "FourLegged -> " + super.toString}class Cat extends Animal with Furry with FourLegged { override def toString = "Cat -> " + super.toString}val question = new Cat

a) Cat -> FourLegged -> HasLegs -> Furry -> Animalb) Cat -> HasLegs -> FourLegged -> Furry -> Animalc) Cat -> Furry -> FourLegged -> HasLegs -> Animald) Cat -> Furry -> HasLegs -> FourLegged -> Animal

Traits Experiment

Page 35: TI1220 Lecture 8: Traits & Type Parameterization

Type Parameterization

Page 36: TI1220 Lecture 8: Traits & Type Parameterization

def append[T](xs: List[T], ys: List[T]): List[T] = xs match { case List() => ys case x :: xs1 => x :: append(xs1, ys) }

Page 37: TI1220 Lecture 8: Traits & Type Parameterization

def map[A,B](xs: List[A], f: A => B): List[B] = xs match { case List() => List() case y :: ys => f(y) :: map(ys, f)}

Page 38: TI1220 Lecture 8: Traits & Type Parameterization

Generic classes and traits

• Set[T]: generic sets parameterized with type T

• Set[Int]: set of integers, instance of Set[T]

• No raw types: always use with type parameter

Example: Functional Queues

Page 39: TI1220 Lecture 8: Traits & Type Parameterization

typedef struct queue_elem { int val; struct queue_elem *next;} queue_elem;

typedef struct queue { queue_elem *first; queue_elem *last;} queue;

queue *newQueue() { queue *q = (queue *)malloc(sizeof(queue)); q->first = NULL; q->last = NULL; return q;}

Imperative Queue in C

Page 40: TI1220 Lecture 8: Traits & Type Parameterization

void enqueue(queue *q, int val) { queue_elem *elem = (queue_elem *)malloc(sizeof(queue_elem)); elem->val = val; elem->next = NULL; if(q->first == NULL) { q->first = q->last = elem; } else { q->last->next = elem; q->last = elem; }}

Imperative Queue in C

Page 41: TI1220 Lecture 8: Traits & Type Parameterization

int dequeue(queue *q) { if(q == NULL || q->first == NULL) { return 0; } int val = q->first->val; queue_elem *elem = q->first; if(q->first == q->last) { q->last = NULL; } q->first = q->first->next; free(elem); return val;}

Imperative Queue in C

Page 42: TI1220 Lecture 8: Traits & Type Parameterization

Queue operations

• head: return first element

• tail: return rest

• append: new queue with new element at the end

Functional Queue

• fully persistent

• contents not changed when appending

• efficient implementation should be O(1) for all operations

scala> val q = Queue(1, 2, 3)q: Queue[Int] = Queue(1, 2, 3)

scala> val q1 = q append 4q1: Queue[Int] = Queue(1, 2, 3, 4)

scala> qres0: Queue[Int] = Queue(1, 2, 3)

Functional Queue

Page 43: TI1220 Lecture 8: Traits & Type Parameterization

class SlowAppendQueue[T](elems: List[T]) { // Not efficient def head = elems.head def tail = new SlowAppendQueue(elems.tail) def append(x: T) = new SlowAppendQueue(elems ::: List(x))}

Functional Queue (First Attempt)

Page 44: TI1220 Lecture 8: Traits & Type Parameterization

class SlowAppendQueue[T](elems: List[T]) { // Not efficient def head = elems.head def tail = new SlowAppendQueue(elems.tail) def append(x: T) = new SlowAppendQueue(elems ::: List(x))}

append = O(n)

Functional Queue (First Attempt)

Page 45: TI1220 Lecture 8: Traits & Type Parameterization

class SlowAppendQueue[T](elems: List[T]) { // Not efficient def head = elems.head def tail = new SlowAppendQueue(elems.tail) def append(x: T) = new SlowAppendQueue(elems ::: List(x))}

class SlowHeadQueue[T](smele: List[T]) { // Not efficient // smele is elems reversed def head = smele.last def tail = new SlowHeadQueue(smele.init) def append(x: T) = new SlowHeadQueue(x :: smele)}

append = O(n)

Functional Queue (First Attempt)

Page 46: TI1220 Lecture 8: Traits & Type Parameterization

class SlowAppendQueue[T](elems: List[T]) { // Not efficient def head = elems.head def tail = new SlowAppendQueue(elems.tail) def append(x: T) = new SlowAppendQueue(elems ::: List(x))}

class SlowHeadQueue[T](smele: List[T]) { // Not efficient // smele is elems reversed def head = smele.last def tail = new SlowHeadQueue(smele.init) def append(x: T) = new SlowHeadQueue(x :: smele)}

append = O(n)

head, tail = O(n)

Functional Queue (First Attempt)

Page 47: TI1220 Lecture 8: Traits & Type Parameterization

class SlowAppendQueue[T](elems: List[T]) { // Not efficient def head = elems.head def tail = new SlowAppendQueue(elems.tail) def append(x: T) = new SlowAppendQueue(elems ::: List(x))}

class SlowHeadQueue[T](smele: List[T]) { // Not efficient // smele is elems reversed def head = smele.last def tail = new SlowHeadQueue(smele.init) def append(x: T) = new SlowHeadQueue(x :: smele)}

append = O(n)

head, tail = O(n)

head, tail, append = O(1) cannot be possible!

Functional Queue (First Attempt)

Page 48: TI1220 Lecture 8: Traits & Type Parameterization

class Queue[T]( private val leading: List[T], private val trailing: List[T]) { def head = leading.head def tail = new Queue(leading.tail, trailing) def append(x: T) = new Queue(leading, x :: trailing)}

elems == leading ::: trailing.reverse

Represent Queue with Two Lists

Page 49: TI1220 Lecture 8: Traits & Type Parameterization

class Queue[T]( private val leading: List[T], private val trailing: List[T]) { def head = leading.head def tail = new Queue(leading.tail, trailing) def append(x: T) = new Queue(leading, x :: trailing)}

elems == leading ::: trailing.reverse

but what if leading.isEmpty?

Represent Queue with Two Lists

Page 50: TI1220 Lecture 8: Traits & Type Parameterization

Mirroring

class Queue[T]( private val leading: List[T], private val trailing: List[T]) { private def mirror = if (leading.isEmpty) new Queue(trailing.reverse, Nil) else this def head = mirror.leading.head def tail = { val q = mirror new Queue(q.leading.tail, q.trailing) } def append(x: T) = new Queue(leading, x :: trailing)}

Page 51: TI1220 Lecture 8: Traits & Type Parameterization

Mirroring

class Queue[T]( private val leading: List[T], private val trailing: List[T]) { private def mirror = if (leading.isEmpty) new Queue(trailing.reverse, Nil) else this def head = mirror.leading.head def tail = { val q = mirror new Queue(q.leading.tail, q.trailing) } def append(x: T) = new Queue(leading, x :: trailing)}

head, tail, append: O(1)mirror: O(n) but amortized over n calls of tail

Page 52: TI1220 Lecture 8: Traits & Type Parameterization

Mirroring

class Queue[T]( private val leading: List[T], private val trailing: List[T]) { private def mirror = if (leading.isEmpty) new Queue(trailing.reverse, Nil) else this def head = mirror.leading.head def tail = { val q = mirror new Queue(q.leading.tail, q.trailing) } def append(x: T) = new Queue(leading, x :: trailing)}

head, tail, append: O(1)mirror: O(n) but amortized over n calls of tail

implementation is exposed!

Page 53: TI1220 Lecture 8: Traits & Type Parameterization

class Queue[T] private ( private val leading: List[T], private val trailing: List[T]) { def this() = this(Nil, Nil) def this(elems: T*) = this(elems.toList, Nil)

def head = ... def tail = ... def append(x: T) = ...}

scala> Queue(1, 2, 3)

private parameters

public auxiliary constructors

hide implementation details from clients

Private Constructors

Page 54: TI1220 Lecture 8: Traits & Type Parameterization

class Queue[T] private ( private val leading: List[T], private val trailing: List[T]) { def head = ... def tail = ... def append(x: T) = ... }

object Queue { // constructs a queue with initial elements ‘xs’ def apply[T](xs: T*) = new Queue[T](xs.toList, Nil)}

Factory Method hide implementation details from clients

factory method

private parameters

Page 55: TI1220 Lecture 8: Traits & Type Parameterization

trait Queue[T] { def head: T def tail: Queue[T] def append(x: T): Queue[T]}

object Queue { def apply[T](xs: T*): Queue[T] = new QueueImpl[T](xs.toList, Nil)

private class QueueImpl[T]( private val leading: List[T], private val trailing: List[T] ) extends Queue[T] { def mirror = if (leading.isEmpty) new QueueImpl(trailing.reverse, Nil) else this def head: T = mirror.leading.head def tail: QueueImpl[T] = { val q = mirror new QueueImpl(q.leading.tail, q.trailing) } def append(x: T) = new QueueImpl(leading, x :: trailing) }

}

hide implementation details from clients

Page 56: TI1220 Lecture 8: Traits & Type Parameterization

scala> def doesNotCompile(q: Queue) {}<console>:5: error: trait Queue takes type parametersdef doesNotCompile(q: Queue) {}

scala> def doesCompile(q: Queue[AnyRef]) {}doesCompile: (Queue[AnyRef])Unit

Queue is a trait, not a typeQueue is a type constructor or generic trait

Queue[String] is a (specific) type

Generic Traits

Page 57: TI1220 Lecture 8: Traits & Type Parameterization

Queue[String] subtype of Queue[AnyRef] ?

if S subtype of T then Queue[S] subtype of Queue[T] ?

If answer is yes: Queue is covariant in T

trait Queue[+T] { ... }

val q: Queue[AnyRef] = Queue[String](“a”)

If answer is no: Queue is contravariant in T

trait Queue[-T] { ... }

val q: Queue[String] = Queue[AnyRef]() default: nonvariant

Subtyping & Variance Annotations

Page 58: TI1220 Lecture 8: Traits & Type Parameterization

class Cell[+T](init: T) { private[this] var current = init def get = current def set(x: T) { current = x }}

val c1 = new Cell[String]("abc")val c2: Cell[Any] = c1c2.set(1)val s: String = c1.get

Covariance and Mutable Classes

Page 59: TI1220 Lecture 8: Traits & Type Parameterization

class Cell[+T](init: T) { private[this] var current = init def get = current def set(x: T) { current = x }}

val c1 = new Cell[String]("abc")val c2: Cell[Any] = c1c2.set(1)val s: String = c1.get

Cell.scala:7: error: covariant type T occurs incontravariant position in type T of value xdef set(x: T) = current = x

Covariance and Mutable Classes

Page 60: TI1220 Lecture 8: Traits & Type Parameterization

package society { package professional { class Executive { private[professional] var workDetails = null private[society] var friends = null private[this] var secrets = null

def help(another : Executive) { println(another.workDetails) println(another.secrets) //ERROR } } }}

Source: http://www.tutorialspoint.com/scala/scala_access_modifiers.htm

Scope of Protection of Access Modifiers

A private member is visible only inside the class or object that contains the member definition.

A protected member is only accessible from subclasses of the class in which the member is defined.

Every member not labeled private or protected is public. There is no explicit modifier for public members. Such members can be accessed from anywhere.

Page 61: TI1220 Lecture 8: Traits & Type Parameterization

// this is JavaString[] a1 = { "abc" };Object[] a2 = a1;a2[0] = new Integer(17);String s = a1[0];

no compile-time error

Variance and Arrays

Page 62: TI1220 Lecture 8: Traits & Type Parameterization

// this is JavaString[] a1 = { "abc" };Object[] a2 = a1;a2[0] = new Integer(17);String s = a1[0];

Exception in thread "main" java.lang.ArrayStoreException:java.lang.Integer at JavaArrays.main(JavaArrays.java:8)

no compile-time error

Variance and Arrays

Page 63: TI1220 Lecture 8: Traits & Type Parameterization

// this is JavaString[] a1 = { "abc" };Object[] a2 = a1;a2[0] = new Integer(17);String s = a1[0];

Exception in thread "main" java.lang.ArrayStoreException:java.lang.Integer at JavaArrays.main(JavaArrays.java:8)

no compile-time error

motivation: generic treatment of arrays:

void sort(Object[] a, Comparator cmp) { ... }

Variance and Arrays

Page 64: TI1220 Lecture 8: Traits & Type Parameterization

scala> val a1 = Array("abc")a1: Array[java.lang.String] = Array(abc)

scala> val a2: Array[Any] = a1<console>:5: error: type mismatch; found : Array[java.lang.String] required: Array[Any] val a2: Array[Any] = a1 ˆ

scala> val a2: Array[Object] = a1.asInstanceOf[Array[Object]]a2: Array[java.lang.Object] = Array(abc)

Scala Arrays are Non-variant

Page 65: TI1220 Lecture 8: Traits & Type Parameterization

class Queue[+T] { def append(x: T) = ...}

class StrangeIntQueue extends Queue[Int] { override def append(x: Int) = { println(Math.sqrt(x)) super.append(x) }}

val x: Queue[Any] = new StrangeIntQueuex.append("abc")

Checking Variance Annotations

Page 66: TI1220 Lecture 8: Traits & Type Parameterization

class Queue[+T] { def append(x: T) = ...}

class StrangeIntQueue extends Queue[Int] { override def append(x: Int) = { println(Math.sqrt(x)) super.append(x) }}

val x: Queue[Any] = new StrangeIntQueuex.append("abc")

Queues.scala:11: error: covariant type T occurs incontravariant position in type T of value xdef append(x: T) = ˆ

Checking Variance Annotations

Page 67: TI1220 Lecture 8: Traits & Type Parameterization

class Queue[+T]( private val leading: List[T], private val trailing: List[T]) { def append[U >: T](x: U) = new Queue[U](leading, x :: trailing) // ...}class Fruitclass Apple extends Fruitclass Orange extends Fruit

scala> val qa = Queue(new Apple)scala> val qb = qa.append(new Orange)qb: Queue[Fruit] = ...

U >: T == U is a supertype of T

Lower Bounds

Page 68: TI1220 Lecture 8: Traits & Type Parameterization

class Queue[+T] private ( private[this] var leading: List[T], private[this] var trailing: List[T]) { private def mirror() = if (leading.isEmpty) { while (!trailing.isEmpty) { leading = trailing.head :: leading trailing = trailing.tail } } def head: T = { mirror(); leading.head } def tail: Queue[T] = { mirror(); new Queue(leading.tail, trailing) } def append[U >: T](x: U) = new Queue[U](leading, x :: trailing)}

Optimized Functional Queue

Page 69: TI1220 Lecture 8: Traits & Type Parameterization

def orderedMergeSort[T <: Ordered[T]](xs: List[T]): List[T] = { def merge(xs: List[T], ys: List[T]): List[T] = (xs, ys) match { case (Nil, _) => ys case (_, Nil) => xs case (x :: xs1, y :: ys1) => if (x < y) x :: merge(xs1, ys) else y :: merge(xs, ys1) } val n = xs.length / 2 if (n == 0) xs else { val (ys, zs) = xs splitAt n merge(orderedMergeSort(ys), orderedMergeSort(zs)) }}

Upperbounds

Page 70: TI1220 Lecture 8: Traits & Type Parameterization

Information hiding

• private constructors

• factory methods

• object private members

Type variance

• subtyping of generic types

• covariant, contravariant variance annotations

• lower bounds, upper bounds

Type Parameterization Summary

Page 71: TI1220 Lecture 8: Traits & Type Parameterization

Reading & Programming in Week 6

Reading

Scala Chapter 12: Traits

Scala Chapter 19: Type Parameterization

Week 9: Parsers and Interpreters

WebLab: Graded Assignment 2: (deadline 14 May 2013, 23:59)