Rahul
Rahul

Reputation: 47096

scala type parameterization confusion

Consider the following example

abstract class Lookup(val code:String,val description:String)

class USState(code:String, description:String, val area:Symbol)
  extends Lookup(code,description)

class Country(code:String, description:String, val postCode:String)
  extends Lookup(code,description)

class HtmlLookupSelect(data:List[Lookup]) {
  def render( valueMaker:(Lookup) => String ) =
    data.map( (l) => valueMaker(l) )
}

val countries = List(
  new Country("US","United States","USA"),
  new Country("UK","Unites Kingdom","UK"),
  new Country("CA","Canada","CAN"))

def lookupValue(l:Lookup) = l.description
def countryValue(c:Country) = c.description + "(" + c.postCode + ")"

val selector = new HtmlLookupSelect(countries) //Doesn't throw an error
selector.render(countryValue) //Throws an error

HtmlLookupSelect expects a list of Lookup objects as constructor parameter. While creating a HtmlLookupSelect object, a list of county objects are passed to it and the compiler doesn't throw an error since it recognizes Country as a subclass of Lookup

But in the next line, when I try to invoke a method with Country as the parameter type(instead of the expected Lookup), I get a Type mismatch error. Why is this happening?

Upvotes: 0

Views: 55

Answers (1)

Gabriele Petronella
Gabriele Petronella

Reputation: 108101

countryValue is a function from Country to String (actually a method that gets eta-expanded into a function, but not relevant now), i.e. a Function1[Country, String].

render expects a Function1[Lookup, String].

So the question we want to answer is

Given Country is a subtype of Lookup

is Function1[Country, String] a subtype of Function1[Lookup, String]?

In order to answer that, let's look at the definition of Function1:

trait Function1[-T1, +R] extends AnyRef

See the -T1? That's the input parameter and the - means that Function1 is contravariant in its input parameter and covariant in its output parameter.

So, if A <: B (where <: is the subtype relation) and R <: S then

Function1[B, R] <: Function[A, S]

In your example, Country <: Lookup so

Function1[Country, String] </: Function[Lookup, String]

In other words a function is a subtype of another when it promises no less (co-variant return type) and it requires no more (contra-variant input type).

For example: a function that takes an Animal and returns an Apple can be used whenever a function that takes a Dog and returns a Fruit is required. More formally

Dog <: Animal
Fruit <: Apple
Function1[Animal, Apple] <: Function1[Dog, Fruit]

but the opposite isn't true: if your function handles dogs and returns any fruit, it surely can't be used to handle any animal and return an apple.

Upvotes: 3

Related Questions