使用类型类中最低的子类型? [英] Use the lowest subtype in a typeclass?
问题描述
我有以下代码:
sealed trait Animal
case class Cat(name: String) extends Animal
case class Dog(name: String) extends Animal
trait Show[A] {
def show(a: A): String
}
class Processor[A](a: A) {
def print(implicit S: Show[A]): Unit = println(S.show(a))
}
implicit val showCat: Show[Cat] = c => s"Cat=${c.name}"
implicit val showDog: Show[Dog] = d => s"Dog=${d.name}"
val garfield = Cat("Garfield")
val odie = Dog("Odie")
val myPets = List(garfield, odie)
for (p <- myPets) {
val processor = new Processor(p)
processor.print // THIS FAILS AT THE MOMENT
}
有谁知道让 processor.print
行工作的好方法?
Does anyone know of a nice way to get that line processor.print
working?
我能想到 2 个解决方案:
I can think of 2 solutions:
- 模式匹配 for 循环中的
p
. - 创建一个
Show[Animal]
实例,并将其与所有子类型进行模式匹配.
- pattern match the
p
in the for loop. - create an instance of
Show[Animal]
and pattern match it against all its subtypes.
但我想知道是否有更好的方法来做到这一点.
But I'm wondering if there's a better way of doing this.
提前致谢!
推荐答案
编译错误是
could not find implicit value for parameter S: Show[Product with Animal with java.io.Serializable]
你可以让 Animal
扩展 Product
和 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
也不是手动定义隐式Show[Animal]
implicit val showAnimal: Show[Animal] = {
case x: Cat => implicitly[Show[Cat]].show(x)
case x: Dog => implicitly[Show[Dog]].show(x)
// ...
}
您可以使用 Show="nofollow noreferrer">宏
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]
或无形
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))
或木兰
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
@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)
}
}
}
对于 cats.Show
with Kittens你可以只写
implicit val showAnimal: Show[Animal] = cats.derived.semi.show
问题是 List(garfield, odie)
中的 garfield
和 odie
具有相同的类型,它是 Animal
而不是 Cat
和 Dog
.如果您不想为父类型定义类型类的实例,您可以使用类似列表的结构保留单个元素的类型,HList garfield :: odie :: HNil
.
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
.
用于比较 Dotty 中的派生类型类
For comparison deriving type classes in Dotty
这篇关于使用类型类中最低的子类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!