Reputation: 47096
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
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 ofLookup
is
Function1[Country, String]
a subtype ofFunction1[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