I am reading the book https://underscore.io/books/scala-with-cats/ and trying to understand contravariant in Cats.
On the page 68 there is an example of the implementation of Contravariant in Cats
We can summon instances of
Contravariant
using theContravariant.apply
method. Cats provides instances for data types that consume parameters, includingEq
,Show
, andFunction1
. Here’s an example:
import cats.Contravariant
import cats.Show
import cats.instances.string._
object ContraCats {
val showString = Show[String]
val showSymbol = Contravariant[Show]
.contramap(showString)((sym: Symbol) => s"'${sym.name}'")
def main(args: Array[String]): Unit = {
println(showSymbol.show('dave))
}
}
Show is a typeclass not a type, so how it is possible to create an instance of typeclass Contravariant
of the typeclass Show
? And Show
is not a higher kinded type.
The typeclass Show
is declared as Show[T]
.
Contravariant
is declared as Contravariant[F[_]]
If we say that
Int
has kind *
List[Int]
has kind *
List
has kind * -> *
Monad
declared as trait Monad[M[_]]
has kind (* -> *) -> *
then by analogy:
Show
has kind * -> *
Show[Int]
has kind *
Contravariant
has kind (* -> *) -> *
Contravariant[Show]
has kind *
That is, Contravariant
is a thing that takes * -> *
and produces something of kind *
. Since Show
is of kind * -> *
, the type Contravariant[Show]
is valid.
Now, in your code snippet, there is an expression Contravariant[Show]
. It's not a type, and it should not be confused with the application of a type constructor. Desugared, this thing is essentially
Contravariant.apply[Show](inst)
where Contravariant
is the companion object of trait Contravariant
and inst
is an implicitly supplied instance of type Contravariant[Show]
. Again, everything fits together seamlessly.
Remark on nomenclature. I would say that Show
is a "higher kind". It's in no way definitive, but I like the following quote from the Atlassian Blog [emphasis and code indentation mine]:
Furthermore, you can have kinds that are themselves parameterized by higher kinded types. So, something could not only take a type, but take something that itself takes type parameters. An example would be the covariant functor: Functor[F[_]], it has the kind:
((* -> *) -> *)
This says: given a simple higher kinded type, produce the final type. For instance given a type constructor like List produce the final type Functor[List].
From the above quote it is evident that it is at least common to refer to the kind * -> *
of List
as "higher kind". It's definitely higher than *
. It's definitely higher than anything you can write down in current version of Java (if you write List
in Java, the best you can get is a "blah blah the generic type List requires type parameters blah blah"-error).