16

I know about using co- and contravariance in the standard library (e.g. collections and trait Function) I wonder how co- and contravariance are used in design of "real world" business applications.

Jacek Laskowski
  • 72,696
  • 27
  • 242
  • 420
Michael
  • 10,185
  • 12
  • 59
  • 110

2 Answers2

24

The classic example is functions, taking the Scala interface for a function with a single argument:

trait Function1[-T1, +R]

Which is contravariant (the -) for the argument, and covariant (the +) for the return type.

Why?

Imagine you have these classes:

class Timelord { ... }
class Doctor extends Timelord { ... }

class Enemy { ... }
class Dalek extends Enemy { ... }

If you have a method that takes, as a parameter, a Doctor => Enemy function; then it's okay to supply an instance of TimeLord => Enemy. It'll still accept instances of Doctor.

So TimeLord => Enemy is a subclass of Doctor => Enemy because TimeLord is a superclass of Doctor, it's contravariant in that parameter.

Likewise, a function returning a Dalek is valid when you need a function returning some Enemy, because a Dalek is-an Enemy

So Doctor => Dalek is a subclass of Doctor => Enemy because Dalek is a subclass of Enemy, it's covariant in that parameter.

Jacek Laskowski
  • 72,696
  • 27
  • 242
  • 420
Kevin Wright
  • 49,540
  • 9
  • 105
  • 155
  • 1
    AKA, anywhere we need a `Dalek` that can exterminate `Doctor`s, a `Dalek` of `Any` will do :-) – fommil Nov 09 '13 at 23:33
  • 1
    so, according to `Function[-R, +T]`, is `Doctor => Enemy` the most appropriate function signature? I'm saying this because, if R is contravariant, then R should be the most sub-type, and, since T is covariant, it should be the most super-type? – Kevin Meredith Feb 19 '14 at 20:38
9

Essentially anywhere where you want to make use of both parametric polymorphism (generics) and inheritance, you will probably end up wanting either declaration site variance (+/-), use site variance (wildcards), or more likely, both.

Polymorphic types are usually fairly high-level abstractions, so while your domain objects may not need variance annotations, it's likely that code that you write to manipulate your domain objects will need to use variance annotations, at least if your domain objects are part of inheritance hierarchies, which seems very frequent.

If you take a look at essentially any library or framework, you'll find frequent use of variance annotations. If you're abstracting your "real world" application correctly, you'll probably be writing lots of libraries to support it, with a small core of critical business logic nicely decoupled from all of the support infrastructure. All that support infrastructure will probably make frequent use of variance annotations, too.

Kevin Wright
  • 49,540
  • 9
  • 105
  • 155
Kris Nuttycombe
  • 4,560
  • 1
  • 26
  • 29