Использовать самый низкий подтип в классе типов? - PullRequest
0 голосов
/ 23 апреля 2020

У меня есть следующий код:

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 работать?

Я могу придумать 2 решения:

  1. шаблон соответствует p в for для l oop.
  2. создать экземпляр Show[Animal] и шаблон сопоставляет его со всеми его подтипами.

Но мне интересно, есть ли лучший способ сделать это.

Заранее спасибо!

Ответы [ 2 ]

2 голосов
/ 23 апреля 2020

Ошибка компиляции

could not find implicit value for parameter S: Show[Product with Animal with java.io.Serializable]

Вы можете сделать Animal продление Product и 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 для запечатанных черт (с экземплярами для потомков) с помощью макросов

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 с Котятами Вы можете написать просто

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

Дело в том, что garfield и odie in List(garfield, odie) имеют тот же тип, и это Animal вместо Cat и Dog. Если вы не хотите определять экземпляр класса типа для родительского типа, вы можете использовать спископодобную структуру, сохраняющую типы отдельных элементов, HList garfield :: odie :: HNil.

1 голос
/ 23 апреля 2020

Самое общее решение - просто упаковать экземпляры класса типов при создании myPets, экзистенциально

final case class Packaged[+T, +P](wit: T, prf: P)
type WithInstance[T, +P[_ <: T]] = Packaged[U, P[U]] forSome { type U <: T }
implicit def packageInstance[T, U <: T, P[_ <: T]]
                            (wit: U)(implicit prf: P[U])
                          : T WithInstance P
= Packaged(wit, prf)

val myPets = List[Animal WithInstance Show](garfield, odie)
for(Packaged(p, showP) <- myPets) {
    implicit val showP1 = showP
    new Processor(p).print // note: should be def print()(implicit S: Show[A]), so that this can be .print()
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...