TI1220 Lecture 8: Traits & Type Parameterization
Upcoming SlideShare
Loading in...5
×
 

TI1220 Lecture 8: Traits & Type Parameterization

on

  • 1,045 views

 

Statistics

Views

Total Views
1,045
Views on SlideShare
423
Embed Views
622

Actions

Likes
0
Downloads
18
Comments
0

1 Embed 622

http://department.st.ewi.tudelft.nl 622

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    TI1220 Lecture 8: Traits & Type Parameterization TI1220 Lecture 8: Traits & Type Parameterization Presentation Transcript

    • TI1220 2012-2013Concepts of Programming LanguagesEelco Visser / TU DelftLecture 8: Traits & Type Parameterization
    • var ms := course.managers;var ms : Set<Person> := Set<Person>();ms.addAll(course.managers);Analysis => Lecture 12: ConcurrencyRoot CauseThe FixIntention: copysemanticsEffect: referencesemanticsThe Fault: Concurrent writes on CourseEdition
    • The Airconditioning
    • Syntax and SemanticsNames, Bindings, and ScopesStorageData TypesFunctional ProgrammingFirst-class FunctionsPolymorphismTraits & Type ParameterizationParsing and InterpretationData Abstraction / Modular ProgrammingFunctional Programming ReduxConcurrencyConcurrent ProgrammingDomain-Specific LanguagesQuarter 3Quarter 4Basics ofScalaJavaScriptC
    • Traits
    • abstract class Element {def contents: Array[String]def height: Int = contents.lengthdef width: Int = if (height == 0) 0else contents(0).length}class ArrayElement(conts: Array[String]) extends Element {val contents: Array[String] = conts}Classical InheritanceInheriting fields and methods
    • class UniformElement(ch: Char,override val width: Int,override val height: Int) extends Element {private val line = ch.toString * widthdef 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
    • abstract class Element {def demo() {println("Elements implementation invoked")}}class ArrayElement extends Element {override def demo() {println("ArrayElements implementation invoked")}}class LineElement extends ArrayElement {override def demo() {println("LineElements implementation invoked")}}// UniformElement inherits Element’s democlass UniformElement extends Elementdef invokeDemo(e: Element) {e.demo()}scala> invokeDemo(new ArrayElement)ArrayElements implementation invokedscala> invokeDemo(new LineElement)LineElements implementation invokedscala> invokeDemo(new UniformElement)Elements implementation invokedDynamic Binding
    • Subtyping• Polymorphism & dynamic bindingCode Reuse• reuse instance variables and methods from super classSingle Inheritance• cannot reuse code from more than one classInterfaces• support subtyping multiple classes• must re-implement interfaceJava-style Single Inheritance
    • Trait• reusable unit of code• encapsulates method and field definitions• reused by mixing into classes• class can mix in any number of traitsApplications• rich interfaces• stackable modifications
    • 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 typesDefining and Using Traits
    • class Animaltrait HasLegsclass Frog extends Animal with Philosophical with HasLegs {override def toString = "green"override def philosophize() {println("It aint easy being " + toString + "!")}}superclasstraitsoverride code from traitscala> val phrog: Philosophical = new Frogphrog: Philosophical = greenscala> phrog.philosophize()It aint easy being green!Mixing in (multiple) traits
    • Trait is like Java interface with• methods• fields• stateTrait is Scala class• without class parameters• dynamic binding of ‘super’
    • Rich interface• many methods• convenient for client• more work for implementerThin interface• few methods• easy for implementers• inconvenient for clienttrait CharSequence {def charAt(index: Int): Chardef length: Intdef subSequence(start: Int, end: Int): CharSequencedef toString(): String}Rich Interfaces with Traits• small number of abstractmethods implemented byclient• large number of concretemethods inherited by client
    • class Point(val x: Int, val y: Int)class Rectangle(val topLeft: Point, val bottomRight: Point){def left = topLeft.xdef right = bottomRight.xdef width = right - left// and many more geometric methods...}abstract class Component {def topLeft: Pointdef bottomRight: Pointdef left = topLeft.xdef right = bottomRight.xdef width = right - left// and many more geometric methods...}Rectangular objects without traits
    • trait Rectangular {def topLeft: Pointdef bottomRight: Pointdef left = topLeft.xdef right = bottomRight.xdef 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
    • trait Rectangular {def topLeft: Pointdef bottomRight: Pointdef left = topLeft.xdef right = bottomRight.xdef 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 = 10Rectangular objects with traits
    • class Rational(n: Int, d: Int) {// ...def <(that: Rational) =this.numer * that.denom > that.numer * this.denomdef >(that: Rational) = that < thisdef <=(that: Rational) = (this < that) || (this == that)def >=(that: Rational) = (this > that) || (this == that)}defined in terms of <based on standardsemantics of ordering{A Rich Interface for Ordering
    • class Rational(n: Int, d: Int) {// ...def <(that: Rational) =this.numer * that.denom > that.numer * this.denomdef >(that: Rational) = that < thisdef <=(that: Rational) = (this < that) || (this == that)def >=(that: Rational) = (this > that) || (this == that)}defined in terms of <based on standardsemantics 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
    • class Rational(n: Int, d: Int) {// ...def <(that: Rational) =this.numer * that.denom > that.numer * this.denomdef >(that: Rational) = that < thisdef <=(that: Rational) = (this < that) || (this == that)def >=(that: Rational) = (this > that) || (this == that)}defined in terms of <based on standardsemantics 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 reusableimplementation of orderingA Rich Interface for Ordering
    • Class Queue of integers• put: place integer in queue• get: take integer out• first-in first-outModifications• Doubling: double all integers put in queue• Incrementing: increment all integers put in queue• Filtering: filter out negativeStackable Modifications
    • abstract class IntQueue {def get(): Intdef 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
    • abstract class IntQueue {def get(): Intdef 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@24655fscala> queue.put(10)scala> queue.put(20)scala> queue.get()res9: Int = 10scala> queue.get()res10: Int = 20Class Queue
    • trait Doubling extends IntQueue {abstract override def put(x: Int) { super.put(2 * x) }}dynamically boundcan only be mixed intosubclasses of IntQueuemix into classwith concretedefinitionTrait Doubling
    • trait Doubling extends IntQueue {abstract override def put(x: Int) { super.put(2 * x) }}dynamically boundcan only be mixed intosubclasses of IntQueuemix into classwith concretedefinitionscala> class MyQueue extends BasicIntQueue with Doublingdefined class MyQueuescala> val queue = new MyQueuequeue: MyQueue = MyQueue@91f017scala> queue.put(10)scala> queue.get()res12: Int = 20Trait Doubling
    • trait Doubling extends IntQueue {abstract override def put(x: Int) { super.put(2 * x) }}dynamically boundcan only be mixed intosubclasses of IntQueuemix into classwith concretedefinitionscala> class MyQueue extends BasicIntQueue with Doublingdefined class MyQueuescala> val queue = new MyQueuequeue: MyQueue = MyQueue@91f017scala> queue.put(10)scala> queue.get()res12: Int = 20scala> val queue = new BasicIntQueue with Doublingqueue: BasicIntQueue with Doubling = $anon$1@5fa12dscala> queue.put(10)scala> queue.get()res14: Int = 20Trait Doubling
    • 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
    • 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 BasicIntQueuewith Incrementingwith Filtering)scala> queue.put(-1);queue.put(0);queue.put(1)scala> queue.get()res15: Int = 1scala> queue.get()res15: Int = 2Stacking Modifications
    • 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 BasicIntQueuewith Incrementingwith Filtering)scala> queue.put(-1);queue.put(0);queue.put(1)scala> queue.get()res15: Int = 1scala> queue.get()res15: Int = 2Stacking Modificationsscala> val queue = (new BasicIntQueuewith Filteringwith 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
    • // Multiple inheritance thought experimentval q = new BasicIntQueue with Incrementing with Doublingq.put(42) // which put would be called?Multiple Inheritance (Why Not?)Incrementing DoublingBasicIntQueuenew BasicIntQueue withIncrement with Doubling
    • // Multiple inheritance thought experimenttrait MyQueue extends BasicIntQueuewith Incrementing with Doubling {def put(x: Int) {Incrementing.super.put(x)// (Not real Scala)Doubling.super.put(x)}}Multiple Inheritance (Why Not?)Incrementing DoublingBasicIntQueuenew BasicIntQueue withIncrement with Doublingput of BasicIntQuecalled twice!
    • a class is always linearized before allof its superclasses and mixed in traitsclass Animaltrait Furry extends Animaltrait HasLegs extends Animaltrait FourLegged extends HasLegsclass Cat extends Animal with Furry with FourLeggedLinearly Ordering Traits
    • Units of code• reusable through inheritance• can be mixed in at multiple places in hierarchyMultiple inheritance ++• calls to super are linearized• avoid diamond problem• stack changesTraits Summary
    • 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 Cata) Cat -> FourLegged -> HasLegs -> Furry -> Animalb) Cat -> HasLegs -> FourLegged -> Furry -> Animalc) Cat -> Furry -> FourLegged -> HasLegs -> Animald) Cat -> Furry -> HasLegs -> FourLegged -> AnimalTraits Experiment
    • Type Parameterization
    • def append[T](xs: List[T], ys: List[T]): List[T] =xs match {case List() => yscase x :: xs1 => x :: append(xs1, ys)}
    • 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)}
    • 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 parameterExample: Functional Queues
    • 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
    • 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
    • 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
    • Queue operations• head: return first element• tail: return rest• append: new queue with new element at the endFunctional Queue• fully persistent• contents not changed when appending• efficient implementation should be O(1) for all operationsscala> 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
    • class SlowAppendQueue[T](elems: List[T]) { // Not efficientdef head = elems.headdef tail = new SlowAppendQueue(elems.tail)def append(x: T) = new SlowAppendQueue(elems ::: List(x))}Functional Queue (First Attempt)
    • class SlowAppendQueue[T](elems: List[T]) { // Not efficientdef head = elems.headdef tail = new SlowAppendQueue(elems.tail)def append(x: T) = new SlowAppendQueue(elems ::: List(x))}append = O(n)Functional Queue (First Attempt)
    • class SlowAppendQueue[T](elems: List[T]) { // Not efficientdef head = elems.headdef 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 reverseddef head = smele.lastdef tail = new SlowHeadQueue(smele.init)def append(x: T) = new SlowHeadQueue(x :: smele)}append = O(n)Functional Queue (First Attempt)
    • class SlowAppendQueue[T](elems: List[T]) { // Not efficientdef head = elems.headdef 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 reverseddef head = smele.lastdef tail = new SlowHeadQueue(smele.init)def append(x: T) = new SlowHeadQueue(x :: smele)}append = O(n)head, tail = O(n)Functional Queue (First Attempt)
    • class SlowAppendQueue[T](elems: List[T]) { // Not efficientdef head = elems.headdef 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 reverseddef head = smele.lastdef 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)
    • class Queue[T](private val leading: List[T],private val trailing: List[T]) {def head = leading.headdef tail = new Queue(leading.tail, trailing)def append(x: T) = new Queue(leading, x :: trailing)}elems == leading ::: trailing.reverseRepresent Queue with Two Lists
    • class Queue[T](private val leading: List[T],private val trailing: List[T]) {def head = leading.headdef tail = new Queue(leading.tail, trailing)def append(x: T) = new Queue(leading, x :: trailing)}elems == leading ::: trailing.reversebut what if leading.isEmpty?Represent Queue with Two Lists
    • Mirroringclass Queue[T](private val leading: List[T],private val trailing: List[T]) {private def mirror =if (leading.isEmpty)new Queue(trailing.reverse, Nil)elsethisdef head = mirror.leading.headdef tail = {val q = mirrornew Queue(q.leading.tail, q.trailing)}def append(x: T) =new Queue(leading, x :: trailing)}
    • Mirroringclass Queue[T](private val leading: List[T],private val trailing: List[T]) {private def mirror =if (leading.isEmpty)new Queue(trailing.reverse, Nil)elsethisdef head = mirror.leading.headdef tail = {val q = mirrornew 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
    • Mirroringclass Queue[T](private val leading: List[T],private val trailing: List[T]) {private def mirror =if (leading.isEmpty)new Queue(trailing.reverse, Nil)elsethisdef head = mirror.leading.headdef tail = {val q = mirrornew 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 tailimplementation is exposed!
    • 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 parameterspublic auxiliary constructorshide implementationdetails from clientsPrivate Constructors
    • 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 implementationdetails from clientsfactory methodprivate parameters
    • trait Queue[T] {def head: Tdef 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 thisdef head: T = mirror.leading.headdef tail: QueueImpl[T] = {val q = mirrornew QueueImpl(q.leading.tail, q.trailing)}def append(x: T) = new QueueImpl(leading, x :: trailing)}}hide implementationdetails from clients
    • 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])UnitQueue is a trait, not a typeQueue is a type constructor or generic traitQueue[String] is a (specific) typeGeneric Traits
    • 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 Ttrait Queue[+T] { ... }val q: Queue[AnyRef] = Queue[String](“a”)If answer is no: Queue is contravariant in Ttrait Queue[-T] { ... }val q: Queue[String] = Queue[AnyRef]()default: nonvariantSubtyping &Variance Annotations
    • class Cell[+T](init: T) {private[this] var current = initdef get = currentdef set(x: T) { current = x }}val c1 = new Cell[String]("abc")val c2: Cell[Any] = c1c2.set(1)val s: String = c1.getCovariance and Mutable Classes
    • class Cell[+T](init: T) {private[this] var current = initdef get = currentdef set(x: T) { current = x }}val c1 = new Cell[String]("abc")val c2: Cell[Any] = c1c2.set(1)val s: String = c1.getCell.scala:7: error: covariant type T occurs incontravariant position in type T of value xdef set(x: T) = current = xCovariance and Mutable Classes
    • package society {package professional {class Executive {private[professional] var workDetails = nullprivate[society] var friends = nullprivate[this] var secrets = nulldef help(another : Executive) {println(another.workDetails)println(another.secrets) //ERROR}}}}Source: http://www.tutorialspoint.com/scala/scala_access_modifiers.htmScope of Protection of Access ModifiersA 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.
    • // this is JavaString[] a1 = { "abc" };Object[] a2 = a1;a2[0] = new Integer(17);String s = a1[0];no compile-time errorVariance and Arrays
    • // 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 errorVariance and Arrays
    • // 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 errormotivation: generic treatment of arrays:void sort(Object[] a, Comparator cmp) { ... }Variance and Arrays
    • 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
    • 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")CheckingVariance Annotations
    • 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) =ˆCheckingVariance Annotations
    • 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 Fruitscala> val qa = Queue(new Apple)scala> val qb = qa.append(new Orange)qb: Queue[Fruit] = ...U >: T == U is a supertype of TLower Bounds
    • 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 :: leadingtrailing = 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
    • 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, _) => yscase (_, Nil) => xscase (x :: xs1, y :: ys1) =>if (x < y) x :: merge(xs1, ys)else y :: merge(xs, ys1)}val n = xs.length / 2if (n == 0) xselse {val (ys, zs) = xs splitAt nmerge(orderedMergeSort(ys), orderedMergeSort(zs))}}Upperbounds
    • Information hiding• private constructors• factory methods• object private membersType variance• subtyping of generic types• covariant, contravariant variance annotations• lower bounds, upper boundsType Parameterization Summary
    • Reading & Programming in Week 6ReadingScala Chapter 12: TraitsScala Chapter 19: Type ParameterizationWeek 9: Parsers and InterpretersWebLab:Graded Assignment 2: (deadline 14 May 2013, 23:59)