Members of Show can be presented as strings.

Cats’ equivalent for the Show typeclass is Show:

import cats._, cats.syntax.all._
// res0: String = "3"

// 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:

(new {}).toString
// res2: String = "repl.MdocSession1@891c355"
(new {}).show
// 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:

case class Person(name: String)
case class Car(model: String)

  implicit val personShow =[Person](
// res4: String = "Alice"

  implicit val carShow = Show.fromToString[Car]
// res5: Car = Car(model = "CR-V")