Поиск типов на основе их членов абстрактного типа

#scala #generics #shapeless

#scala #обобщения #бесформенный

Вопрос:

У меня есть следующие определения типов:

 trait Content
trait Wrapper {
  type ContentType 
}

final case class Foo(param: String) extends Content
final case class Bar(param: String) extends Content

final case class FooWrapper(foo: Foo) extends Wrapper { type ContentType = Foo }
final case class BarWrapper(bar: Bar) extends Wrapper { type ContentType = Bar }
  

Учитывая значение содержимого во время выполнения, я хотел бы вернуть его в соответствующем типе оболочки. Я попробовал следующее, используя Shapeless:

 def fetchWrapper[N, G <: Wrapper](
    implicit
    gen: Generic.Aux[G, N :: HNil],

    // this also compiles, as an alternative to Generics.Aux
    // =:= :G#ValueType =:= N
) = ...
  

Это работает, но только если я явно указываю параметры типа : fetchWrapper[Foo, FooWrapper] . Как мне воспользоваться неявным разрешением для обобщения вещей, чтобы я мог получить правильную оболочку для данного содержимого?

Я думал о создании экземпляра оболочки, используя тот же метод вывода в разделе генератора случайных чисел книги shapeless (т.Е. typelcass, который выдает BarWrapper , если у меня есть неявное Bar :: HNil значение в области видимости), но я даже не могу найти правильный тип оболочки в первую очередь.

Ответ №1:

Generic может легко помочь с преобразованием Wrapper подтипа в Content подтип, но вы хотите наоборот.

Попробуйте класс типа

 trait ContentToWrapper[C <: Content] {
  type Out <: Wrapper { type ContentType = C }
}
object ContentToWrapper {
  implicit val foo: ContentToWrapper[Foo] { type Out = FooWrapper } = null
  implicit val bar: ContentToWrapper[Bar] { type Out = BarWrapper } = null
}

def fetchWrapper[C <: Content](implicit ctw: ContentToWrapper[C]): ctw.Out = ???
  

Если вы создаете Wrapper sealed, вы можете получить класс type

 import shapeless.{Coproduct, Generic, HList, Poly1, poly}
import shapeless.ops.coproduct.ToHList
import shapeless.ops.hlist.CollectFirst

object ContentToWrapper {
  implicit def mkContentToWrapper[C <: Content, WC <: Coproduct, WL <: HList](implicit
    generic: Generic.Aux[Wrapper, WC],
    toHList: ToHList.Aux[WC, WL], // there is CollectFirst for HList but not for Coproduct
    collect: CollectFirst[WL, WrapperSubtypePoly[C]]
  ): ContentToWrapper[C] { type Out = collect.Out } = null

  trait WrapperSubtypePoly[C] extends Poly1
  object WrapperSubtypePoly {
    implicit def cse[C, A <: Wrapper { type ContentType = C }]: 
      poly.Case1.Aux[WrapperSubtypePoly[C], A, A] = poly.Case1(identity)
  }
}
  

Тестирование:

 val w1 = fetchWrapper[Foo]
w1: FooWrapper
val w2 = fetchWrapper[Bar]
w2: BarWrapper
  

Комментарии:

1. Спасибо. ToList и CollectFirst это то, что я искал.