使用类型类中最低的子类型? [英] Use the lowest subtype in a typeclass?

查看:47
本文介绍了使用类型类中最低的子类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码:

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:

  1. 模式匹配 for 循环中的 p.
  2. 创建一个 Show[Animal] 实例,并将其与所有子类型进行模式匹配.
  1. pattern match the p in the for loop.
  2. 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 扩展 ProductSerializable

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-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)
    }
  }
}

对于 cats.Show with Kittens你可以只写

implicit val showAnimal: Show[Animal] = cats.derived.semi.show

问题是 List(garfield, odie) 中的 garfieldodie 具有相同的类型,它是 Animal 而不是 CatDog.如果您不想为父类型定义类型类的实例,您可以使用类似列表的结构保留单个元素的类型,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

如何访问参数列表dotty 宏中的 case 类

这篇关于使用类型类中最低的子类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆