如何为带类型参数的样例类推导Generic.Aux(Shapeless)
Scala泛型编程:为(A, Long)生成指定Coproduct的Generic实例
给定代码
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), TimestampedData[Foo] :+: TimestampedData[Bar] :+: CNil],让Generic将(A, Long)(A <: Data)映射为上述Coproduct类型?
解决方案
可以借助shapeless库的泛型能力实现,步骤如下:
1. 引入依赖
首先确保项目中添加shapeless依赖(以sbt为例):
libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
2. 实现自定义Generic实例
利用shapeless自动为sealed trait生成的Coproduct表示,结合类型转换完成实例定义:
import shapeless._ // 自动获取Data的Coproduct类型表示 type DataCoproduct = Generic[Data]#Repr // 等价于 Foo :+: Bar :+: CNil // 将DataCoproduct中的每个类型包装为TimestampedData type TimestampedDataCoproduct = DataCoproduct#Aux[TimestampedData] // 为(A, Long)实现Generic实例 implicit def timestampedDataGeneric[A <: Data]: Generic.Aux[(A, Long), TimestampedDataCoproduct] = new Generic[(A, Long)] { override type Repr = TimestampedDataCoproduct override def to(tuple: (A, Long)): Repr = { val (data, ts) = tuple // 先将Data转为对应Coproduct元素,再包装为TimestampedData Generic[Data].to(data).map(TimestampedData(_, ts)) } override def from(coproduct: Repr): (A, Long) = { // 从Coproduct中提取TimestampedData,拆解为元组 coproduct.eliminate( tsFoo => (tsFoo.data, tsFoo.timestamp), remaining => remaining.eliminate( tsBar => (tsBar.data, tsBar.timestamp), _ => throw new IllegalArgumentException("无效的Coproduct元素") ) ) } }
说明
- shapeless会自动为
sealed trait Data生成Generic[Data]实例,其Repr就是Foo :+: Bar :+: CNil。 - 通过
DataCoproduct#Aux[TimestampedData]可以将Coproduct中的每个类型X转换为TimestampedData[X],得到目标Coproduct类型。 - 自定义的
Generic实例中,to方法负责将(A, Long)转换为目标Coproduct,from方法则完成反向转换。
内容的提问来源于stack exchange,提问作者eitaporra
相关产品推荐
相关产品推荐

