Members of Show can be presented as strings.

Cats’ equivalent for the Show typeclass is Show:

scala> import cats._, cats.instances.all._,
import cats._
import cats.instances.all._

res0: String = 3

scala> "hello".show
res1: String = hello

Here’s the typeclass contract:

@typeclass trait Show[T] {
  def show(f: T): String

At first, it might seem silly to define Show because Scala already has toString on Any. Any also means anything would match the criteria, so you lose type safety. The toString could be junk supplied by some parent class:

scala> (new {}).toString
res2: String = $anon$1@3d50801

scala> (new {}).show
<console>:21: error: value show is not a member of AnyRef
       (new {}).show

object Show provides two functions to create a Show instance:

object Show {
  /** creates an instance of [[Show]] using the provided function */
  def show[A](f: A => String): Show[A] = new Show[A] {
    def show(a: A): String = f(a)

  /** creates an instance of [[Show]] using object toString */
  def fromToString[A]: Show[A] = new Show[A] {
    def show(a: A): String = a.toString

  implicit val catsContravariantForShow: Contravariant[Show] = new Contravariant[Show] {
    def contramap[A, B](fa: Show[A])(f: B => A): Show[B] =
      show[B]( _ compose f)

Let’s try using them:

scala> :paste
// Entering paste mode (ctrl-D to finish)
case class Person(name: String)
case class Car(model: String)

// Exiting paste mode, now interpreting.

defined class Person
defined class Car

scala> implicit val personShow =[Person](
personShow: cats.Show[Person] = cats.Show$$anon$2@1bbdc034

scala> Person("Alice").show
res4: String = Alice

scala> implicit val carShow = Show.fromToString[Car]
carShow: cats.Show[Car] = cats.Show$$anon$3@778bc812

scala> Car("CR-V")
res5: Car = Car(CR-V)