Show 

LYAHFGG:

ある値は、その値が Show 型クラスのインスタンスになっていれば、文字列として表現できます。

Cats で Show に対応する型クラスは Show だ:

scala> import cats._, cats.data._, cats.implicits._
import cats._
import cats.data._
import cats.implicits._

scala> 3.show
res0: String = 3

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

これが型クラスのコントラクトだ:

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

Scala には既に AnytoString があるため、Show を定義するのは馬鹿げているように一見見えるかもしれない。 Any ということは逆に何でも該当してしまうので、型安全性を失うことになる。 toString は何らかの親クラスが書いたゴミかもしれない:

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

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

object ShowShow のインスタンスを作成するための 2つの関数を提供する:

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](fa.show _ compose f)
  }
}

使ってみる:

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 = Show.show[Person](_.name)
personShow: cats.Show[Person] = cats.Show$$anon$2@1e4eb40b

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

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

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

Contents