% CSE 130: Spring 2012
% Generics: Subtyping & Parametric Polymorphism 
% Ranjit Jhala, UC San Diego 

## Today's Plan: A Tale of Two Polymorphisms 

### Polymorphism Enables Reuse

- "Polymorphism" = "many kinds"
- Same functions operate on **many kinds** of data
- Same containers hold **many kinds** of data 

### A Tale of Two Polymorphisms

> - **Subtyping** Polymorphism ("is-a" style, classic OO)

> - **Parametric** Polymorphism ("type variables",  ML style)

> - Scala combines them in interesting ways

## Today's Plan: A Tale of Two Polymorphisms 

> 1. **Parametric** Polymorphism ("type variables",  ML style)

> 2. **Subtyping + Parametric** Polymorphism

> 3. **Implicitly** retrofitting behavior to existing types 

## Parametric Polymorphism in Scala

- **Functions**

- Data Types

## Parametric Polymorphism in Scala

Much like ML (and Java/C# Generics)

- Scala's creator Odersky also did Java generics...

Except that you have to **write down** the type variables

### In Ocaml

~~~~~{.ocaml}
let id x = x 
~~~~~

### In Scala

~~~~~{.scala}
def id[A](x: A): A = x
~~~~~

## Parametric Polymorphism in Scala

Much like ML (and Java/C# Generics)

- Scala's creator Odersky also did Java generics...

Except that you have to **write down** the type variables

### In Ocaml

~~~~~{.ocaml}
let swap p = (snd p, fst p)
~~~~~

### In Scala

~~~~~{.scala}
def swap[A, B](p: (A, B)) = (p._2, p._1)
~~~~~





## Parametric Polymorphism in Scala

Much like ML (and Java/C# Generics)

- Scala's creator Odersky also did Java generics...

Except that you have to **write down** the type variables

### In Ocaml

~~~~~{.ocaml}
let rec length xs = match xs with
  | []   -> 0
  | _::t -> 1 + length t
~~~~~

### In Scala

~~~~~{.scala}
def length[A](xs: List[A]): Int = xs match {
    case Nil      => 0
    case (_ :: t) => 1 + length(t)
  }
~~~~~

## Parametric Polymorphism in Scala

Much like ML (and Java/C# Generics)

- Scala's creator Odersky also did Java generics...

Except that you have to **write down** the type variables

### In Ocaml 

~~~~~{.ocaml}
let rec map f xs = match xs with
  | []   -> [] 
  | x::t -> (f x) :: map (f, t) 
~~~~~

### In Scala 

~~~~~{.scala}
def map[A, B](f: A => B)(xs: List[A]): List[B] = xs match {
    case Nil      => List() 
    case (_ :: t) => (f(x))::(map(f)(rest))
  }
~~~~~

## QUIZ: Are these two functions equivalent?

~~~~~{.scala}
//Subtype Polymorphism
def headAny(xs: List[Any]): Any = xs match { 
  case h :: _ => h 
}

//Parametric Polymorphism
def headPoly[A](xs: List[A]): A = xs match { 
  case h :: _ => h 
}
~~~~~

**A.** **Yes**

**B.** **No** because `headAny` **accepts more** inputs

**C.** **No** because `headPoly` **accepts more** inputs

**D.** **No** because `headAny` **generates more** outputs

**E.** **No** because `headPoly` **generates more** outputs


## Parametric Polymorphism in Scala

- Functions

- **Data Types**


## Parametric Polymorphism in Scala

Lets write a **polymorphic** library for representing (*cough* Sets!)

- [UnorderedBag.scala](lec-generics/UnorderedBag.scala)

~~~~~{.scala}
sealed abstract class Bag[A] 
case class Empty[A]()                    extends Bag[A]
case class Plus[A](elt: A, rest: Bag[A]) extends Bag[A]
~~~~~

The above is very similar to what you might write in Ocaml

~~~~~{.ocaml}
type 'a bag = Empty | Plus of ('a * 'a bag)
~~~~~

## Parametric Polymorphism in Scala

Lets write a **polymorphic** library for representing (*cough* Sets!)

- [UnorderedBag.scala](lec-generics/UnorderedBag.scala)

~~~~~{.scala}
sealed abstract class Bag[A]
case class Empty[A]()                    extends Bag[A]
case class Plus[A](elt: A, rest: Bag[A]) extends Bag[A]
~~~~~

### Case Class Are Just Vanilla Classes

> - But no need for pesky `new` to create instances
> - But also upporting **pattern matching**
> - ML Data Constructors == OO Sub Classes


## Parametric Polymorphism in Scala

Lets write a **polymorphic** library for representing (*cough* Sets!)

- [UnorderedBag.scala](lec-generics/UnorderedBag.scala)

~~~~~{.scala}
sealed abstract class Bag[A]
case class Empty[A]()                    extends Bag[A]
case class Plus[A](elt: A, rest: Bag[A]) extends Bag[A]
~~~~~

<img src="lec-generics/BagHier.png" width=400 align="middle"/> 

## Parametric Polymorphism in Scala

Lets write a **polymorphic** library for representing (*cough* Sets!)

We can fill in various methods...

~~~~~{.scala}
sealed abstract class Bag[A] {
  def size : Int = 
    this match {
      case Empty()       => 0
      case Plus(_, rest) => 1 + rest.size
    }
  // ...
}
~~~~~

> -  **Note** We use `this` to pattern match against the **receiver** bag

## Parametric Polymorphism in Scala

Lets write a **polymorphic** library for representing (*cough* Sets!)

We can fill in various methods...

~~~~~{.scala}
sealed abstract class Bag[A] {
  // ...
  def contains(x: A) : Boolean = {
    this match {
      case Empty()                => false
      case Plus(e, _) if (x == e) => true  
      case Plus(_, rest)          => rest.contains(x) 
    }
  }
  // ...
}
~~~~~

> - **Note** Type parameter `A` is **in scope** in entire class definition

## Parametric Polymorphism in Scala

Lets write a **polymorphic** library for representing (*cough* Sets!)

We can fill in various methods...

~~~~~{.scala}
sealed abstract class Bag[A] {
  // ...
  def add(x: A) : Bag[A] = {
    if (this.contains(x)) this else { Plus(x, this) }
  }
  // ...
}
~~~~~

**Note** Type parameter `A` is **in scope** in entire class definition

> - Same type of `Bag` returned as output...

> - **Immutable**: returns a **brand new** `Bag`

## Parametric Polymorphism in Scala

Lets write a **polymorphic** library for representing (*cough* Sets!)

We can fill in various methods...

~~~~~{.scala}
sealed abstract class Bag[A] {
  // ...
  def gimme: Option[A] = 
    this match {
      case Plus(x, rest) => Some(x)
      case _             => None
    }
  // ...
}
~~~~~

**Note** **Type parameter** `A` is **in scope** in entire class definition

> - Same kind of element returned as output...

> - **Immutable**: element **not removed** from bag.

# Go To [Code](lec-generics/UnorderedBag.scala)

## Today's Plan: A Tale of Two Polymorphisms 

1. Parametric Polymorphism ("type variables",  ML style)

2. **Subtyping + Parametric** Polymorphism

3. Implicitly retrofitting behavior to existing types 


## QUIZ: What is the value of res?

~~~~~{.scala}
def findMin[A](cur: A, xs: List[A]): A = 
  xs match { 
    case Nil               => cur
    case h::t if (h < cur) => findMin(h, t) 
    case _::t              => findMin(cur, t)
  }

val res = findMin(1000, List(20, 4, 1, 6))
~~~~~

**A.** Type Error in `findMin(1000, ...)` 

**B.** Type Error in definition of `findMin`

**C.** Type Error in `List(20,4,1,6)`

**D.** `1`

**E.** `20`

## `Bag` is a pretty lame data structure

- `contains`
- `add`
- etc.

Must walk over **entire** bag to determine if elements are present.

> - Let us arrange the elements in **increasing** order ...

> - ... can tell if an element is **absent** when we find a **bigger** one.

## An Ordered Bag

Lets change `contains` to **utilize** ordering property:

~~~~~{.scala}
sealed abstract class Bag[A] {
  // ...
  def contains(x: A) : Boolean = {
    this match {
      case Empty()                => false
      case Plus(e, _) if (x == e) => true  
      case Plus(e, _) if (x  > e) => false 
      case Plus(_, rest)          => rest.contains(x) 
    }
  }
  // ...
}
~~~~~

## Compiler is not happy

~~~~~{.scala}
OrderedBag.scala:16: error: value > is not a member of type parameter A
      case Plus(e, _) if (x  > e) => false 
~~~~~

> - <img src="lec-generics/yuno_compiler.jpg" width=550 align="middle"/> 

## Compiler is not happy ... as we've made a mistake!!!

~~~~~{.scala}
OrderedBag.scala:16: error: value > is not a member of type parameter A
      case Plus(e, _) if (x  > e) => false 
~~~~~

### Remember: Types Are Contracts ...

> - Compiler says `contains` **requires** more properties from `A`...
> - **Cannot** implement `contains` **for every** type `A` ...
> - ... only for types **that support comparision**

~~~~~{.scala}
sealed abstract class Bag[A] {
  def contains(x: A) : Boolean = {
    this match {
      case Plus(e, _) if (x  > e) => false 
    }
  }
}
~~~~~

## Compiler is not happy ... as we've made a mistake!!!

~~~~~{.scala}
OrderedBag.scala:16: error: value > is not a member of type parameter A
      case Plus(e, _) if (x  > e) => false 
~~~~~

### Remember: Types Are Contracts ...

> - <img src="lec-generics/yuno_programmer.jpg" width=475 align="middle"/> 

# Need to describe types supporting comparison...

## A Trait Describing Types Supporting Comparison

[Ord.scala](lec-generics/Ord.scala)

~~~~~{.scala}
trait Ord[A] {
  //Must Be Implemented
  def cmp(that: A): Int 
  
  //Automatically derived from cmp 
  def ===(that: A): Boolean = (this cmp that) == 0
  def <(that: A): Boolean   = (this cmp that) < 0
  def >(that: A): Boolean   = (this cmp that) > 0
  def <=(that: A): Boolean  = (this cmp that) <= 0
  def >=(that: A): Boolean  = (this cmp that) <= 0
}
~~~~~

### Very much like Java **interfaces** 

- Except that from the one requirement `cmp` ...
- ... we get a bunch of methods for **free** 


## An Ordered Bag

~~~~~{.scala}
sealed abstract class Bag[A <: Ord[A]] {
  //...
}
~~~~~

### Using trait `Ord` to State Requirements

> - For every `A` that is a **subtype** of `Ord[A]` (i.e. that **implements** `Ord[A]`)

> - We define the class and methods of `Bag[A]`


## An Ordered Bag

### We can do comparisons on `A` in `contains`

Now we can go ahead and do comparisons on `A` 

~~~~~{.scala}
sealed abstract class Bag[A <: Ord[A]] {
  // ...
  def contains(x: A) : Boolean = {
    this match {
      case Empty()                => false
      case Plus(e, _) if (x == e) => true  
      case Plus(_, rest)          => rest.contains(x) 
    }
  }
  // ...
}
~~~~~

## An Ordered Bag

### Change the `add` method so objects are put in the right position

~~~~~{.scala}
sealed abstract class Bag[A <: Ord[A]] {
  //...
  def add(x: A) : Bag[A] = 
    this match {
      case Plus(e, rest) if (x > e)  => Plus(e, rest.add(x))
      case Plus(e, _)    if (x == e) => this
      case _                         => Plus(x, this)
    }
  //...
}
~~~~~

## An Ordered Bag

### Change the `remove` method to make it more efficient

No need to go to the end

~~~~~{.scala}
sealed abstract class Bag[A <: Ord[A]] {
  def remove(x: A) : Bag[A] = {
    this match {
      case Plus(e, rest) if (x <  e) => this // no need to look further
      case Empty()                   => this
      case Plus(e, rest) if (x == e) => rest 
      case Plus(e, rest)             => Plus(e, rest.remove(x))
    }
  }
}
~~~~~

# Go To [Code](lec-generics/OrderedBag.scala) 

## Recap: Subtyping + Parametric Polymorphism

Define function, class, etc. for any type `A` that **is a subtype of** something

~~~~~{.scala}
sealed abstract class Bag[A <: Ord[A]] {
  //...
}
~~~~~

### Bounded Quantification

- Type is defined **for every** `A` that **satisfies a condition** 


## QUIZ: What is the value of res?

~~~~~{.scala}
def findMin[A <: Ord[A]](cur: A, xs: List[A]): A = 
  xs match { 
    case Nil               => cur
    case h::t if (h < cur) => findMin(h, t) 
    case _::t              => findMin(cur, t)
  }

val res = findMin(1000, List(20, 4, 1, 6))
~~~~~

**A.** Type Error in `findMin(1000, ...)` 

**B.** Type Error (in definition of `findMin`)

**C.** Type Error (in `List(20, 4, 1, 6)`)

**D.** `1`

**E.** `20`

## How to add functionality to existing types?

Using [FixedOrderedBag.scala](lec-generics/FixedOrderedBag.scala) we get

~~~~~{.scala}
$ make fobag
$ scala

scala> Bag(1,2,3)
<console>:8: error: inferred type arguments [Int] do not conform to 
method apply's type parameter bounds [A <: Ord[A]]
       Bag(1,2,3)
~~~~~

## How to add functionality to existing types?

Using [FixedOrderedBag.scala](lec-generics/FixedOrderedBag.scala) we get

~~~~~{.scala}
$ make fobag
$ scala

scala> Bag(1,2,3)
<console>:8: error: inferred type arguments [Int] do not conform to 
method apply's type parameter bounds [A <: Ord[A]]
       Bag(1,2,3)
~~~~~

### Cannot always **predict** future uses of a type

> - We just made up a brand new trait `Ord` ...

> - ... so of course the **existing** type `Int` does not implement `Ord`!!!

> - Could make our own re-implementations of `Int` but thats lame...


## Today's Plan: A Tale of Two Polymorphisms 

1. Parametric Polymorphism ("type variables",  ML style)

2. Subtyping + Parametric Polymorphism

3. **Implicitly retrofitting behavior to existing types** 

## How to add functionality to existing types?

### Cannot always **predict** future uses of a type

- We just made up a brand new trait `Ord` ...

- ... so of course the **existing** type `Int` does not implement `Ord`!

> - **Solution:** Use a **proxy** that does the comparison


## Retrofitting Functionality with [Proxies](lec-generics/Ord.scala)

~~~~~{.scala}
def findMin[A](cur: A, xs: List[A])(proxy: A => Ord[A]): A = 
  xs match { 
    case Nil                      => cur
    case h::t if (proxy(h) < cur) => findMin(h, t) 
    case _::t                     => findMin(cur, t)
  }
~~~~~

> - Now, the `Int` need not **itself** implement `Ord[Int]` 
>     - i.e. `Int` need not support comparisons with `Int`

> - But should be some **proxy** that is able to do comparisons on its behalf...
>     - `proxy(h) < cur` instead of `h < cur`

> - Next, lets **create** such a proxy function!


## Retrofitting Functionality with [Proxies](lec-generics/Ord.scala)

~~~~~{.scala}
def findMin[A](cur: A, xs: List[A])(proxy: A => Ord[A]): A = 
  xs match { 
    case Nil                      => cur
    case h::t if (proxy(h) < cur) => findMin(h, t) 
    case _::t                     => findMin(cur, t)
  }
~~~~~

### Creating a Proxy Function

~~~~~{.scala}
def intProxy(x: Int) = new Ord[Int] {
  def cmp(that: Int) : Int = x - that
}
~~~~~


> - **Proxy** maps `x: Int` to an `Ord[Int]` supporting comparisons with `x`

> - Thus `proxy(h) < cur` will hold exactly when `h` is less than `cur`

## Retrofitting Functionality with [Proxies](lec-generics/Ord.scala)

~~~~~{.scala}
def findMin[A](cur: A, xs: List[A])(proxy: A => Ord[A]): A = 
  xs match { 
    case Nil                      => cur
    case h::t if (proxy(h) < cur) => findMin(h, t)(proxy) 
    case _::t                     => findMin(cur, t)(proxy)
  }
~~~~~

### Creating A Proxy Function

~~~~~{.scala}
def intProxy(x: Int) = new Ord[Int] {
  def cmp(that: Int) : Int = x - that
}
~~~~~

### Using A Proxy Function

~~~~~{.scala}
scala> import ProxyDemo._ 
scala> findMin(1000, List(20, 4, 1, 6))(intProxy)
res1: Int = 1
~~~~~

## Retrofitting Functionality with [Proxies](lec-generics/Ord.scala)

### Create Proxies For Other Types Too...

~~~~~{.scala}
def stringProxy(x: String) = new Ord[String] {
  def cmp(that: String) : Int = x compare that
}
~~~~~

### Using The Proxy Function

~~~~~{.scala}
scala> import ProxyDemo._ 
scala> findMin("z", List("apple", "chorizo", "machaca", "adobado"))(stringProxy)
res1: String = adobado 
~~~~~


## Retrofitting Functionality with [Proxies](lec-generics/Ord.scala)

### Proxies are awesome ... 

> - Add functionality to existing types by letting us **view**
>     - `Int` as implementation of `Ord[Int]`
>     - `String` as implementations of `Ord[String]`

### ... but Proxies are also Lame 

> - Proxies **require** us to
>     - Pass around the relevant proxy functions ...
>     - Clutter code with ugly conversions ...

# Strategy (for life!): focus on awesome, cut the lame...

## Implicit Parameters and Values

### Mark certain function params as implicit

~~~~~{.scala}
def sayHello(implicit n: String) = println("Hello " + n) 
~~~~~


## Implicit Parameters and Values

### Mark certain function params as implicit

~~~~~{.scala}
def sayHello(implicit n: String) = println("Hello " + n) 
~~~~~

### Mark certain values as implicit

~~~~~{.scala}
implicit val defaultName = "Ranjit"
~~~~~


## Implicit Parameters and Values

### Mark certain function params as implicit

~~~~~{.scala}
def sayHello(implicit n: String) = println("Hello " + n) 
~~~~~

### Mark certain values as implicit

~~~~~{.scala}
implicit val defaultName = "Ranjit"
~~~~~

### Can call the functions in the usual way

~~~~~{.scala}
scala> sayHello("Roberto")
Hello Roberto
~~~~~

## Implicit Parameters and Values

### Mark certain function params as implicit

~~~~~{.scala}
def sayHello(implicit n: String) = println("Hello " + n) 
~~~~~

### Mark certain values as implicit

~~~~~{.scala}
implicit val defaultName = "Ranjit"
~~~~~

### Can call the functions in the usual way

~~~~~{.scala}
scala> sayHello("Roberto")
Hello Roberto
~~~~~


### ... otherwise Scala fills in the blanks!

~~~~~{.scala}
scala> sayHello
Hello Ranjit
~~~~~

## Implicits: The Magic of Types

Scala uses types to fill in the blanks

> - When **missing** a value of type `T`

> - Scala **automatically** substitutes implicit `T` value ...

> - ... if such a value **is in scope**.

## Implicit Conversions: Automatically "Fix" Type Errors

Suppose I wrote a function expecting `String` inputs

~~~~~{.scala}
scala> def yu(msg: String) = println("Y U  NO " + msg)
yu: (msg: String)Unit
~~~~~


## Implicit Conversions: Automatically "Fix" Type Errors

Suppose I wrote a function expecting `String` inputs

~~~~~{.scala}
scala> def yu(msg: String) = println("Y U  NO " + msg)
yu: (msg: String)Unit
~~~~~

Naturally, this is fine ...

~~~~~{.scala}
scala> yu("GIVE EASY FINAL")
Y U  NO GIVE EASY FINAL
~~~~~


## Implicit Conversions: Automatically "Fix" Type Errors

Suppose I wrote a function expecting `String` inputs

~~~~~{.scala}
scala> def yu(msg: String) = println("Y U  NO " + msg)
yu: (msg: String)Unit
~~~~~

Naturally, this is fine ...

~~~~~{.scala}
scala> yu("GIVE EASY FINAL")
Y U  NO GIVE EASY FINAL
~~~~~

... but this throws an error

~~~~~{.scala}
scala> yu(10)
<console>:9: error: type mismatch;
 found   : Int(10)
 required: String
       yu(10)
          ^
~~~~~

## Implicit Conversions: Automatically "Fix" Type Errors

Suppose I wrote a function expecting `String` inputs

~~~~~{.scala}
scala> def yu(msg: String) = println("Y U  NO " + msg)
yu: (msg: String)Unit
~~~~~

But if we add an **implicit conversion**

~~~~~{.scala}
scala> implicit def i2s(i: Int) = i.toString
i2s: (i: Int)java.lang.String
~~~~~


## Implicit Conversions: Automatically "Fix" Type Errors

Suppose I wrote a function expecting `String` inputs

~~~~~{.scala}
scala> def yu(msg: String) = println("Y U  NO " + msg)
yu: (msg: String)Unit
~~~~~

But if we add an **implicit conversion**

~~~~~{.scala}
scala> implicit def i2s(i: Int) = i.toString
i2s: (i: Int)java.lang.String
~~~~~

Then lo and behold!

~~~~~{.scala}
scala> yu(10)       
Y U  NO 10
~~~~~

> - `yu(10)`  **automatically fixed** to `yu(i2s(10))` 

> - Using the type mismatch and type of `i2s`


## Retrofitting Functionality with [Proxies](lec-generics/Ord.scala)

- Proxies **require** we pass around functions & clutter code...

- ... but **implicits** automatically insert proxies where needed!

## Retrofitting Functionality with [Implicit Proxies](lec-generics/Ord.scala)

**Step 1:** Make proxy **parameters** implicit 

~~~~~{.scala}
def findMin[A](cur: A, xs: List[A])(implicit proxy: A => Ord[A]): A = 
  xs match { 
     case Nil                      => cur
     case h::t if (proxy(h) < cur) => findMin(h, t)(proxy) 
     case _::t                     => findMin(cur, t)(proxy)
  }
~~~~~

## Retrofitting Functionality with [Implicit Proxies](lec-generics/Ord.scala)

**Step 2:** Remove explicit **uses** of `proxy` (Scala automatically inserts them!)

~~~~~{.scala}
def findMin[A](cur: A, xs: List[A])(implicit proxy: A => Ord[A]): A = 
  xs match { 
     case Nil                        => cur
     case h::t if (/*proxy*/h < cur) => findMin(h, t)/* (proxy) */ 
     case _::t                       => findMin(cur, t)/* (proxy) */
  }
~~~~~

## Retrofitting Functionality with [Implicit Proxies](lec-generics/Ord.scala)

**Step 2:** Remove explicit **uses** of `proxy` (Scala automatically inserts them!)

~~~~~{.scala}
def findMin[A](cur: A, xs: List[A])(implicit proxy: A => Ord[A]): A = 
  xs match { 
     case Nil               => cur
     case h::t if (h < cur) => findMin(h, t) 
     case _::t              => findMin(cur, t)
  }
~~~~~

## Retrofitting Functionality with [Implicit Proxies](lec-generics/Ord.scala)

**Step 3:** Make proxy functions **implicit**

~~~~~{.scala}
  implicit def intProxy(x: Int) = new Ord[Int] {
    def cmp(that: Int) : Int = x - that
  }
 
  implicit def stringProxy(x: String) = new Ord[String] {
    def cmp(that: String) : Int = x compare that
  }
~~~~~

## Retrofitting Functionality with [Implicit Proxies](lec-generics/Ord.scala)

After steps

1. Make proxy **parameters** implicit 

2. Remove explicit **uses** of `proxy` (Scala automatically inserts them!)

3. Make proxy functions **implicit**



## Retrofitting Functionality with [Implicit Proxies](lec-generics/Ord.scala)

After steps

1. Make proxy **parameters** implicit 

2. Remove explicit **uses** of `proxy` (Scala automatically inserts them!)

3. Make proxy functions **implicit**

We get:

~~~~~{.scala}
scala> val res = findMin(1000, List(10, 2, 30, 14)) // look ma, no proxies!
res: Int = 2
~~~~~

## Retrofitting Functionality with [Implicit Proxies](lec-generics/Ord.scala)


### This is a common enough pattern to warrant special syntax.

Instead of:

~~~~~{.scala}
def findMin[A](cur: A, xs: List[A])(implicit proxy: A => Ord[A]): A
~~~~~

we can just write the **equivalent**

~~~~~{.scala}
def findMin[A <% Ord[A]](cur: A, xs: List[A]): A
~~~~~

## Retrofitting Functionality with [Implicit Proxies](lec-generics/Ord.scala)


### This is a common enough pattern to warrant special syntax.

Instead of:

~~~~~{.scala}
def findMin[A](cur: A, xs: List[A])(implicit proxy: A => Ord[A]): A
~~~~~

we can just write the **equivalent**

~~~~~{.scala}
def findMin[A <% Ord[A]](cur: A, xs: List[A]): A
~~~~~

<br>

### View Bound

- `A <: Ord[A]`  

- Means: For any `A` with an **implicit** proxy mapping `A` to `Ord[A]`

## Retrofitting Functionality with [Implicit Proxies](lec-generics/Ord.scala)

We can now make a real [ordered bag](lec-generics/ViewBag.scala)

~~~~~{.scala}
sealed abstract class Bag[A <% Ord[A]] {

  def contains(x: A) : Boolean = {
    this match {
      case Empty()                => false
      case Plus(e, _) if (x == e) => true  
      case Plus(e, _) if (x  > e) => false 
      case Plus(_, rest)          => rest.contains(x) 
    }
  }

  // etc.
~~~~~

## Retrofitting Functionality with [Implicit Proxies](lec-generics/Ord.scala)

We can now make a real [ordered bag](lec-generics/ViewBag.scala)

We make a **companion object** that makes it easy to create `Bag`s

~~~~~{.scala}
/* A Companion Object that has various key methods */
object Bag {
  def apply[A <% Ord[A]](xs: A*) : Bag[A] = { 
    val s0 : Bag[A] = Empty()
    xs.toList.foldLeft(s0)(_ add _)  
  }
}
~~~~~

**Note:** the view bound on `apply`

## Retrofitting Functionality with [Implicit Proxies](lec-generics/Ord.scala)

We can now make a real [ordered bag](lec-generics/ViewBag.scala)

We make a **companion object** that makes it easy to create `Bag`s

We can finally use `Bag` with existing types!

~~~~~{.scala}
scala> val b0 = Bag(3,1,4,2)
<console>:7: error: No implicit view available from Int => Ord[Int].
       val b0 = Bag(3,1,4,2)
                   ^
~~~~~

> - Eh? Oh! We forgot to put the implicit proxies **in scope**...

## Retrofitting Functionality with [Implicit Proxies](lec-generics/Ord.scala)

First, lets **import the proxies** into scope ...

~~~~~{.scala}
scala> import OrdInstances._
~~~~~

## Retrofitting Functionality with [Implicit Proxies](lec-generics/Ord.scala)

First, lets **import the proxies** into scope ...

~~~~~{.scala}
scala> import OrdInstances._
~~~~~

And we're off...

~~~~~{.scala}
scala> val b0 = Bag(3,1,2) // Now it can insert the intProxy 
b0: Bag[Int] = 1,2,3,4     // Note how the Bag is ordered

scala> b0 gimme
res0: Option[Int] = Some(1)

scala> b0 remove 1 gimme
res1: Option[Int] = Some(2)

scala> b0 remove 1 remove 2 gimme
res2: Option[Int] = Some(3)

scala> b0 remove 1 remove 2 remove 3 gimme
res3: Option[Int] = None
~~~~~


## Retrofitting Functionality with [Implicit Proxies](lec-generics/Ord.scala)

We can use **any type** for which an implicit proxy is defined:

~~~~~{.scala}
scala> val b1 = Bag("donkey", "monkey", "cat")
b1: Bag[java.lang.String] = cat,donkey,monkey

scala> b1 contains "monkey"
res: Boolean = true

scala> b1 contains "hippo"
res: Boolean = false 

scala> val b2 = b1 add "hippo"
b2: Bag[java.lang.String] = cat,donkey,hippo,monkey

scala> b2 contains "hippo"
res: Boolean = true
~~~~~

> - **Exercise:** What will `b1 contains "hippo"` return now?

## Retrofitting Functionality with [Implicit Proxies](lec-generics/Ord.scala)

### The Coolest Part: Bootstrapping proxies

Scala will automatically **generate** new proxies (if we teach it how)

~~~~~{.scala}
implicit def tup2Proxy[A <% Ord[A], B <% Ord[B]](x: (A, B)) 
  = new Ord[(A, B)] {
      def cmp(that: (A, B)) : Int = { 
        val c1 = x._1 cmp that._1
        if (c1 != 0) c1 else { x._2 cmp that._2 } 
      }
    }
~~~~~

## Retrofitting Functionality with [Implicit Proxies](lec-generics/Ord.scala)

### The Coolest Part: Bootstrapping proxies

Scala will automatically **generate** new proxies (if we teach it how)

~~~~~{.scala}
implicit def tup2Proxy[A <% Ord[A], B <% Ord[B]](x: (A, B)) 
  = new Ord[(A, B)] {
      def cmp(that: (A, B)) : Int = { 
        val c1 = x._1 cmp that._1
        if (c1 != 0) c1 else { x._2 cmp that._2 } 
      }
    }
~~~~~

<br>

### Means:
    
- **If** you have an (implicit) proxies for type `A` and type `B`

- **Then** you have an (implicit) proxy for type `(A, B)`


## Retrofitting Functionality with [Implicit Proxies](lec-generics/Ord.scala)

### The Coolest Part: Bootstrapping proxies

And now we can make `Bag`s of all sorts of pairs (over `Int` and `String`) 

~~~~~{.scala}
scala> val tb = Bag((3, "cat"), (2, "horse"), (1, "zebra"), (0, "giraffe"))
tb: Bag[(Int, java.lang.String)] = (0,giraffe),(1,zebra),(2,horse),(3,cat)

scala> val tb = Bag((2, (1, "cat")), (30, (20, "horse")), (11, (7, "zebra")))
tb: Bag[(Int, (Int, java.lang.String))] = (2,(1,cat)),(11,(7,zebra)),(30,(20,horse))
~~~~~

> - **Note:** last case is truly bootstrapped -- we get nested tuples for free!

> - **Exercise:** Add proxy generators for `List`, `Set`, and `Map`



## Today's Plan: A Tale of Two Polymorphisms 

1. **Parametric Polymorphism** ("type variables",  ML style)

    > - `def add[A](x: A): Bag[A]`

2. **Subtyping + Parametric** Polymorphism

    > - `def add[A <: Ord[A]](x: A): Bag[A]` 

3. **Implicitly retrofitting** behavior to existing types

    > - `def add[A <% Ord[A]](x: A): Bag[A]`

> - Types enable **reuse** without compromising **safety**

# P.S.: Implicits in [PA6](../homeworks/hw6.html) [Json Conversion](../static/pa6/Json.scala)

# Next Time: Prolog...

