#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
это то, что я искал.