问题描述
我有以下类型定义:
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]
。如何利用隐式分辨率对事物进行概括,以便可以为给定的内容派生正确的包装器?
我当时想在无形书的random number generator部分中使用相同的派生技术生成包装器的实例(即,如果我有一个隐式{{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
密封,则可以派生类型类
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