Compile error is
could not find implicit value for parameter S: Show[Product with Animal with java.io.Serializable]
You can make Animal extend Product and Serializable
sealed trait Animal extends Product with Serializable
https://typelevel.org/blog/2018/05/09/product-with-serializable.html
Also instead of defining implicit Show[Animal] manually
implicit val showAnimal: Show[Animal] = {
case x: Cat => implicitly[Show[Cat]].show(x)
case x: Dog => implicitly[Show[Dog]].show(x)
// ...
}
you can derive Show for sealed traits (having instances for descendants) with macros
def derive[A]: Show[A] = macro impl[A]
def impl[A: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
import c.universe._
val typA = weakTypeOf[A]
val subclasses = typA.typeSymbol.asClass.knownDirectSubclasses
val cases = subclasses.map{ subclass =>
cq"x: $subclass => _root_.scala.Predef.implicitly[Show[$subclass]].show(x)"
}
q"""
new Show[$typA] {
def show(a: $typA): _root_.java.lang.String = a match {
case ..$cases
}
}"""
}
implicit val showAnimal: Show[Animal] = derive[Animal]
or Shapeless
implicit val showCnil: Show[CNil] = _.impossible
implicit def showCcons[H, T <: Coproduct](implicit
hShow: Show[H],
tShow: Show[T]
): Show[H :+: T] = _.eliminate(hShow.show, tShow.show)
implicit def showGen[A, C <: Coproduct](implicit
gen: Generic.Aux[A, C],
show: Show[C]
): Show[A] = a => show.show(gen.to(a))
or Magnolia
object ShowDerivation {
type Typeclass[T] = Show[T]
def combine[T](ctx: CaseClass[Show, T]): Show[T] = null
def dispatch[T](ctx: SealedTrait[Show, T]): Show[T] =
value => ctx.dispatch(value) { sub =>
sub.typeclass.show(sub.cast(value))
}
implicit def gen[T]: Show[T] = macro Magnolia.gen[T]
}
import ShowDerivation.gen
or Scalaz-deriving
@scalaz.annotation.deriving(Show)
sealed trait Animal extends Product with Serializable
object Show {
implicit val showDeriving: Deriving[Show] = new Decidablez[Show] {
override def dividez[Z, A <: TList, ShowA <: TList](tcs: Prod[ShowA])(
g: Z => Prod[A]
)(implicit
ev: A PairedWith ShowA
): Show[Z] = null
override def choosez[Z, A <: TList, ShowA <: TList](tcs: Prod[ShowA])(
g: Z => Cop[A]
)(implicit
ev: A PairedWith ShowA
): Show[Z] = z => {
val x = g(z).zip(tcs)
x.b.value.show(x.a)
}
}
}
For cats.Show with Kittens you can write just
implicit val showAnimal: Show[Animal] = cats.derived.semi.show
The thing is that garfield and odie in List(garfield, odie) have the same type and it's Animal instead of Cat and Dog. If you don't want to define instance of type class for parent type you can use list-like structure preserving types of individual elements, HList garfield :: odie :: HNil.
For comparison deriving type classes in Scala 3
How to access parameter list of case class in a dotty macro