[英]How to derive a Generic.Aux if the case class has a type parameter - Shapeless
給出:
sealed trait Data
final case class Foo() extends Data
final case class Bar() extends Data
final case class TimestampedData[A <: Data](data: A, timestamp: Long)
有沒有一種簡潔的方法來生成,例如,一個Generic.Aux
需要
(A, Long)
其中A <: Data
並輸出這個Coproduct
:
TimestampedData[Foo] :+: TimestampedData[Bar] :+: CNil
( Generic.Aux[(A, Long), TimestampedData[Foo]:+: TimestampedData[Bar]:+: CNil]
)
?
不幸的是,由於我不太了解泛型編程,而且由於缺乏資源,我沒有嘗試太多。 我什至不確定如何解決這個問題。
謝謝
您可以嘗試使用PartiallyApplied
模式的方法
import shapeless.{Coproduct, DepFn2, Generic, HList}
import shapeless.ops.coproduct.{Inject, ToHList}
import shapeless.ops.hlist.{Mapped, ToCoproduct}
def toTimestamped[A <: Data] = new PartiallyApplied[A]
class PartiallyApplied[A <: Data] {
def apply[C <: Coproduct,
L <: HList,
L1 <: HList,
C1 <: Coproduct](data: A, timestamp: Long)(implicit
generic: Generic.Aux[Data, C],
toHList: ToHList.Aux[C, L],
mapped: Mapped.Aux[L, λ[A => TimestampedData[A with Data]], L1],
toCoproduct: ToCoproduct.Aux[L1, C1],
inject: Inject[C1, TimestampedData[A]],
): C1 = inject(TimestampedData[A](data, timestamp))
}
val x = toTimestamped(Foo(), 1L) // Inr(Inl(TimestampedData(Foo(),1)))
val y = toTimestamped(Bar(), 1L) // Inl(TimestampedData(Bar(),1))
type Coprod = TimestampedData[Bar] :+: TimestampedData[Foo] :+: CNil
x: Coprod // compiles
y: Coprod // compiles
或類型類1 2 3 4 5 (通常,這是比方法更靈活的解決方案,盡管現在似乎沒有方法優於方法,因為類型類的唯一實例)
trait ToTimestamped[A <: Data] extends DepFn2[A, Long] {
type Out <: Coproduct
}
object ToTimestamped {
type Aux[A <: Data, Out0 <: Coproduct] = ToTimestamped[A] { type Out = Out0 }
def instance[A <: Data, Out0 <: Coproduct](f: (A, Long) => Out0): Aux[A, Out0] =
new ToTimestamped[A] {
override type Out = Out0
override def apply(data: A, timestamp: Long): Out0 = f(data, timestamp)
}
implicit def mkToTimestamped[A <: Data,
C <: Coproduct,
L <: HList,
L1 <: HList,
C1 <: Coproduct](implicit
generic: Generic.Aux[Data, C],
toHList: ToHList.Aux[C, L],
mapped: Mapped.Aux[L, λ[A => TimestampedData[A with Data]], L1],
toCoproduct: ToCoproduct.Aux[L1, C1],
inject: Inject[C1, TimestampedData[A]],
): Aux[A, C1] =
instance((data, timestamp) => inject(TimestampedData[A](data, timestamp)))
}
def toTimestamped[A <: Data](data: A, timestamp: Long)(implicit
toTimestampedInst: ToTimestamped[A]
): toTimestampedInst.Out = toTimestampedInst(data, timestamp)
測試:
val x = toTimestamped(Foo(), 1L) // Inr(Inl(TimestampedData(Foo(),1)))
val y = toTimestamped(Bar(), 1L) // Inl(TimestampedData(Bar(),1))
type Coprod = TimestampedData[Bar] :+: TimestampedData[Foo] :+: CNil
implicitly[ToTimestamped.Aux[Foo, Coprod]] // compiles
x: Coprod // compiles
y: Coprod // compiles
在 Shapeless 中,有Mapped
用於HList
但沒有Coproduct
,因此我必須在類型級別Coproduct
轉換為HList
並返回。
λ[A =>...]
是kind-projector語法。 Mapped
接受類型構造函數F[_]
但TimestampedData
是上限F[_ <: Data]
,因此我不得不使用具有交集類型 ( with
) 的類型 lambda。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.