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
join
is instead ofcombine
,split
is instead ofdispatch
scastie.scala-lang.org/DmytroMitin/05e9N9PLRba9htqVZGW1CA/1libraryDependencies += "com.softwaremill.magnolia1_2" %% "magnolia" % "1.1.2"
github.com/softwaremill/magnolia/tree/scala2 github.com/softwaremill/magnolia – Suited