0

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 the Contravariant.apply method. Cats provides instances for data types that consume parameters, including Eq , Show , and Function1 . 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.

softshipper
  • 32,463
  • 51
  • 192
  • 400
  • How is `Show` not a higher kinded type? It's declared as [`Show[T]`](https://typelevel.org/cats/api/cats/Show.html), it has a type parameter `T`. The question is void. – Andrey Tyukin Feb 07 '18 at 14:39
  • Andrey, `Show[T]` has kind `* -> *`. Is this considered higher kinded, or does the kind need to be `(* -> *) -> *` to be considered higher kinded? – earldouglas Feb 07 '18 at 15:18
  • @earldouglas I've addressed your comment in my answer below. I'd say "yes", it's a higher kind. The thing is: it's essentially irrelevant whether you call `* -> *` or `(* -> *) -> *` "higher kinded", because if there is no `(* -> *) -> *`, then thingies of kind `* -> *` do not make any sense, because there is nothing that could consume them and give back something concrete of simple kind `*`. What is important is that "the language has support for higher kinds": this automatically implies that there must be ways to construct `* -> *` and thingies of kind `(* -> *) -> *` which can consume them. – Andrey Tyukin Feb 07 '18 at 17:37

1 Answers1

4

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).

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
  • @zero_coding: There is a trait `Show`, declared as `trait Show[T]`. The type constructor `Show` has kind `* -> *`. Then there is a companion object, also called `Show`, which appears in your code as `val showString = Show.apply[String]`. Since there are implicit instances of `Show`, it can be *considered a typeclass*. There is no keyword / runtime data structure "typeclass" in Scala, it's not Haskell. A "typeclass" is just a type constructor and some implicitly provided instances of it. – Andrey Tyukin Feb 08 '18 at 13:58
  • What does it mean `trait Show[T]` extends `Show.ContravariantShow[T]`? On `Show.ContravariantShow[T]` there is a function `def show(t: T): String`, what do I have with it on `trait Show[T]`? – softshipper Feb 08 '18 at 14:42
  • It means that `trait Show[T]` extends the trait `ContravariantShow[T]`, which is declared in the companion object `Show`. `Show[T]` is essentially the same thing as `ContravariantShow[-T]`, but with variance erased. It has exactly the same method `def show(t: T): String` as `ContravariantShow[-T]`, but `Show[T]` is declared invariant in the type parameter `T`, that's the whole difference. – Andrey Tyukin Feb 08 '18 at 14:53
  • @zero_coding: Just look at the source code of [Show.scala](https://github.com/typelevel/cats/blob/master/core/src/main/scala/cats/Show.scala), both `Show` and `ContravariantShow[-T]` are there, it's all in the first 20 lines. Just completely usual trait inheritance. No type-level computations, no implicit-magic, no macros, nothing fancy at all. Good old `extends`, the same as in Java, just a little more refined because of declaration-site variance annotations. – Andrey Tyukin Feb 08 '18 at 14:58