I know what covariance and contravariance of types are. My question is why haven't I encountered discussion of these concepts yet in my study of Haskell (as opposed to, say, Scala)?
It seems there is a fundamental difference in the way Haskell views types as opposed to Scala or C#, and I'd like to articulate what that difference is.
Or maybe I'm wrong and I just haven't learned enough Haskell yet :-)
There are two main reasons:
However, the concepts do apply--for instance, the lifting operation performed by fmap
for Functor
instances is actually covariant; the terms co-/contravariance are used in Category Theory to talk about functors. The contravariant
package defines a type class for contravariant functors, and if you look at the instance list you'll see why I said it's much less common.
There are also places where the idea shows up implicitly, in how manual conversions work--the various numeric type classes define conversions to and from basic types like Integer
and Rational
, and the module Data.List
contains generic versions of some standard functions. If you look at the types of these generic versions you'll see that Integral
constraints (giving toInteger
) are used on types in contravariant position, while Num
constraints (giving fromInteger
) are used for covariant position.
There are no "sub-types" in Haskell, so covariance and contravariance don't make any sense.
In Scala, you have e.g. Option[+A]
with the subclasses Some[+A]
and None
. You have to provide the covariance annotations +
to say that an Option[Foo]
is an Option[Bar]
if Foo extends Bar
. Because of the presence of sub-types, this is necessary.
In Haskell, there are no sub-types. The equivalent of Option
in Haskell, called Maybe
, has this definition:
data Maybe a = Nothing | Just a
The type variable a
can only ever be one type, so no further information about it is necessary.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With