Another Tour of Scala

TypeVariance

The Gist

Variances gives a very high-level overview of implementing contravariance and covariance with respect to Generics. Examples and definitions below.

My Interpretation

Scala allows more flexible use of generics than Java. Specifically, you can decide that if Child extends Parent, then Foo[Child] is a subtype of Foo[Parent] (called covariance). You can also decide that Foo[Child] is, instead, a supertype of Foo[Parent] (called contravariance).

Covariance

As mentioned in ScalaGenerics , if we have a generic type ConfigurationOption[T], and two classes, Child which extends Parent, the class ConfigurationOption[Child] is not a subclass of ConfigurationOption[Parent]. Scala provides a way to make this happen, however:

In the definition of class Configuration, we use the +T to indicate that that T is a covariant type. This means that, in our example, Configuration[AdvancedStringValue] is a proper subclass of Configuration[StringValue]. If the +T were changed to a simple T, this code would not compile.

Contravariance

Given that we have covariance, Contravariance exists to solve some problems that can arise when designing certain classes. Scala’s function aspects rely heavily on this.

Suppose we have a class Foo[T]. If we change it to Foo[+T], now Foo[Child] is a subtype of Foo[Parent]. If, instead, we were to declare Foo as Foo[-T], that would mean the opposte: Foo[Child] is a supertype of Foo[Parent].

Why would we need or want to do this?

An example should illuminate why. It’s a bit long, so bear with me (this is a more complicated concept than some of the others being discussed).

Suppose we have a set of classes for dealing with lookup data, such as a list of U.S. States (defined by a code, a description, and an ‘area of the country’) and a list of Countries (defined by a code, a description, and an official postal abbreviation).

Since both expose a code and a description, we pull these up into a superclass called Lookup. Further, we also have a class that creates HTML <SELECT> controls based upon a list of Lookup objects and it exposes a render method that allows us to pass in a function to control the human-readable value for any given Lookup instance.

This code doesn’t compile, even though you might thing it should.

You might think that since Country is a subclass of Lookup, then the function countryValue should be a subtype of the function lookupValue and that you should, therefore, be able to pass it in to render. Especially since selector was created around a list of Country objects!

So, why can’t you?

Consider a slight change to our main application code to the following:

The code still won’t compile, but it makes more sense now why it shouldn’t: you wouldn’t want to use countrySelector on a USState, because USState has no postCode attribute.

So, what’s going on here? Let’s look at the definition of the function type. Our function

valueMaker:(Lookup) => String

could be written as

valueMaker:Function1[Lookup,String]

Function1 is declared as Function1[-P,+R], meaning that Function1[Lookup,String] is actually a subtype of Function1[Country,String].

Now, consider this code:

This code compiles.

Here, we’ve created a special CountrySelect that operates just on Country objects. Of course, countryValue works, but notice how lookupValue also can be passed. Since lookupValue is a subtype of countryValue, we can safely pass it in.

This is because the code using the function valueMaker is definitely going to send it a Country object. Since lookupValue accepts any Lookup, and since Country is a subtype of Lookup, we know for a fact that lookupValue can safely operate on a Country object. Therefore, it it safe to pass in to render.

This took me quite a while to totally get. But, it’s all in aid of maintaining static types while providing flexible syntax and features…exactly what Scala is all about. The cool thing about that, as compared to a similar implementation in a dynamic language (like Ruby) is that the compiler can catch the screwup we made by changing line 21 to use USState instead of Country.

My Thoughts on this Feature

This took me a while to grok, and I hope the example above helps demonstrate why its needed and what it means. This is where static typing starts to get really confusing, although things tend to work out right. The Ruby way of dealing with this is, well, to not deal with it; you are on your own to pass the right thing in. If you don’t, hopefully your unit tests cover it.

Scala lets you document your intent and have the compiler complain when the code violates it. It’s tricky, however, to read scaladoc and/or compiler output and know what’s wrong.