Scala:克服隐式解析的泛型类型擦除
Scala: overcome generic type erasure for implicit resolution
让我先解释一下我的用例。我在数据存储中有一组实体,它们在树结构中相互关联。当树结构中根节点的版本号增加时,相关节点的版本号也必须增加。
这是一组实体
// ADTs
abstract class Versionable[T] { val version: T}
sealed trait PersistableEntity[I] extends Versionable[I] with Product
case class Alpha(version:Int=1) extends PersistableEntity[Int]
case class Beta(version:Int=1) extends PersistableEntity[Int]
case class Gamma(version: Int=1) extends PersistableEntity[Int]
// instances of case classes
val alpha = Alpha()
val beta1 = Beta(100)
val beta2 = Beta(200)
val gamma1 = Gamma(1000)
val gamma2 = Gamma(1100)
val gamma3 = Gamma(1200)
val gamma4 = Gamma(1300)
val gamma5 = Gamma(1400)
我创建了一个类型 class 来保存增加版本号的逻辑。如下图
sealed trait VersionGenerator[E <:PersistableEntity[_]] {
def createNewVersion(entity:E): E
}
object VersionGenerator {
implicit def persistableEntityVersionGenerator[E <: PersistableEntity[Int]: ClassTag]: VersionGenerator[E] =
new VersionGenerator[E] {
type CC = E {def copy(version: Int): E}
override def createNewVersion(persistableEntity: E): E = persistableEntity match {
case caseClassInstance: CC =>
import scala.language.reflectiveCalls
caseClassInstance.copy(version = persistableEntity.version+1)
case _ => persistableEntity
}
}
}
这些工作正常,因为我能够验证它
val alphaVersionGenerator = implicitly[VersionGenerator[Alpha]]
val betaVersionGenerator = implicitly[VersionGenerator[Beta]]
val gammaVersionGenerator = implicitly[VersionGenerator[Gamma]]
// Output
alphaVersionGenerator: VersionGenerator[Alpha] = com.example.A$A107$A$A107$VersionGenerator$$anon@22be8030
betaVersionGenerator: VersionGenerator[Beta] = com.example.A$A107$A$A107$VersionGenerator$$anon@309664a7
gammaVersionGenerator: VersionGenerator[Gamma] = com.example.A$A107$A$A107$VersionGenerator$$anon@c1464bc
到目前为止一切顺利。我还创建了一个隐式 class 并如下所示验证它
implicit class VersionGenerationOps[E <:PersistableEntity[_]](persistableEntity: E)(implicit evidence: VersionGenerator[E]) {
def newVersion: E = evidence.createNewVersion(persistableEntity)
}
val updatedAlpha = alpha.newVersion
这是输出
updatedAlpha: Alpha = Alpha(2)
同样,下面的也可以
def createNewVersions[E <: PersistableEntity[_]](input:E*)(implicit versionGenerator: VersionGenerator[E]): Seq[E] = {
input.map(versionGenerator.createNewVersion)
}
createNewVersions(beta1, beta2, gamma1, gamma2, gamma3, gamma4, gamma5)
// Output
res0: Seq[PersistableEntity[Int] with Serializable] = ArrayBuffer(Beta(101), Beta(201), Gamma(1001), Gamma(1101), Gamma(1201), Gamma(1301), Gamma(1401))
现在是依赖树结构的表示
// ADT
sealed trait DependencyTree[+E <:PersistableEntity[_]]
case class Branch[+E <:PersistableEntity[_]](value: E, incoming: Set[_<:DependencyTree[_<:PersistableEntity[_]]]) extends DependencyTree[E]
case class Leaf[+E <:PersistableEntity[_]](value: E) extends DependencyTree[E]
以及树形结构中表示的实体之间的关系
val myDependencyTree = Branch(alpha, Set(Leaf(gamma1), Leaf(gamma2),
Branch(beta1, Set(Leaf(gamma3), Leaf(gamma4))),
Branch(beta2, Set(Leaf(gamma5)))
))
树的简单遍历工作正常,如下所示
// Simple traversal
def processDependencyTree[E <: PersistableEntity[_]](input: DependencyTree[E]): Stream[_<: PersistableEntity[_]] = {
def processDependencyTreeBFS[T <: PersistableEntity[_]](accumulator: Stream[_ <: PersistableEntity[_]],
dependencyTree: DependencyTree[T]): Stream[_<: PersistableEntity[_]] = {
import VersionGenerator._
dependencyTree match {
case node@Leaf(entity) =>
println(s"Processing leaf: $entity")
Stream(entity)
case node@Branch(entity, dependencies) =>
println(s"Processing branch: $entity")
val processedChildren:Stream[_<: PersistableEntity[_]] = dependencies
.toSeq
.map( (dt: DependencyTree[_ <: PersistableEntity[_]]) => processDependencyTreeBFS(accumulator, dt) )
.reduce((first, second) => first ++ second)
Stream.cons(entity, processedChildren)
}
}
processDependencyTreeBFS(Stream.empty, input)
}
val result = processDependencyTree(myDependencyTree).toList
// Output
Processing branch: Alpha(1)
Processing leaf: Gamma(1000)
Processing leaf: Gamma(1100)
Processing branch: Beta(100)
Processing leaf: Gamma(1200)
Processing leaf: Gamma(1300)
Processing branch: Beta(200)
Processing leaf: Gamma(1400)
result: List[PersistableEntity[_]] = List(Alpha(1), Gamma(1000), Gamma(1100), Beta(100), Gamma(1200), Gamma(1300), Beta(200), Gamma(1400))
好了,问题来了。如果我引入类型为 class 实例的隐式参数,隐式解析会因类型擦除而失败。
这是失败的更新代码
import scala.reflect.runtime.universe._
def processDependencyTree[E <: PersistableEntity[_]](input: DependencyTree[E])
(implicit versionGenerator: VersionGenerator[E], weakTypeTag: WeakTypeTag[E]): Stream[_<: PersistableEntity[_]] = {
def processDependencyTreeBFS[T <: PersistableEntity[_]](accumulator: Stream[_ <: PersistableEntity[_]],
dependencyTree: DependencyTree[T])
(implicit versionGenerator: VersionGenerator[T], weakTypeTag: WeakTypeTag[T]): Stream[_<: PersistableEntity[_]] = {
import VersionGenerator._
dependencyTree match {
case node@Leaf(entity) =>
println(s"Processing leaf: $entity")
Stream(versionGenerator.createNewVersion(entity))
case node@Branch(entity, dependencies) =>
println(s"Processing branch: $entity")
val processedChildren:Stream[_<: PersistableEntity[_]] = dependencies
.toSeq
.map( (dt: DependencyTree[_ <: PersistableEntity[_]]) => processDependencyTreeBFS(accumulator, dt) )
.reduce((first, second) => first ++ second)
Stream.cons(versionGenerator.createNewVersion(entity), processedChildren)
}
}
processDependencyTreeBFS(Stream.empty, input)
}
val result = processDependencyTree(myDependencyTree).toList
这里是错误
Error:(84, 88) could not find implicit value for parameter versionGenerator: A$A189.this.VersionGenerator[_]
.map( (dt: DependencyTree[_ <: PersistableEntity[_]]) => processDependencyTreeBFS(accumulator, dt) )
Error:(84, 88) not enough arguments for method processDependencyTreeBFS: (implicit versionGenerator: A$A189.this.VersionGenerator[_], implicit weakTypeTag: reflect.runtime.universe.WeakTypeTag[_])Stream[_ <: A$A189.this.PersistableEntity[_]].
Unspecified value parameters versionGenerator, weakTypeTag.
.map( (dt: DependencyTree[_ <: PersistableEntity[_]]) => processDependencyTreeBFS(accumulator, dt) )
Error:(194, 88) could not find implicit value for parameter versionGenerator: inst$A$A.VersionGenerator[_]
.map( (dt: DependencyTree[_ <: PersistableEntity[_]]) => processDependencyTreeBFS(accumulator, dt) )
Error:(194, 88) not enough arguments for method processDependencyTreeBFS: (implicit versionGenerator: inst$A$A.VersionGenerator[_], implicit weakTypeTag: reflect.runtime.universe.WeakTypeTag[_])Stream[_ <: inst$A$A.PersistableEntity[_]].
Unspecified value parameters versionGenerator, weakTypeTag.
.map( (dt: DependencyTree[_ <: PersistableEntity[_]]) => processDependencyTreeBFS(accumulator, dt) )
任何修复隐式解析的帮助将不胜感激。非常感谢您的时间和努力!
我找到了答案。由于类型 class 仅针对特定类型声明,即
E <: PersistableEntity[Int]
解析成功的隐式参数,也必须匹配,不能使用
T <: PersistableEntity[_]
让我先解释一下我的用例。我在数据存储中有一组实体,它们在树结构中相互关联。当树结构中根节点的版本号增加时,相关节点的版本号也必须增加。
这是一组实体
// ADTs
abstract class Versionable[T] { val version: T}
sealed trait PersistableEntity[I] extends Versionable[I] with Product
case class Alpha(version:Int=1) extends PersistableEntity[Int]
case class Beta(version:Int=1) extends PersistableEntity[Int]
case class Gamma(version: Int=1) extends PersistableEntity[Int]
// instances of case classes
val alpha = Alpha()
val beta1 = Beta(100)
val beta2 = Beta(200)
val gamma1 = Gamma(1000)
val gamma2 = Gamma(1100)
val gamma3 = Gamma(1200)
val gamma4 = Gamma(1300)
val gamma5 = Gamma(1400)
我创建了一个类型 class 来保存增加版本号的逻辑。如下图
sealed trait VersionGenerator[E <:PersistableEntity[_]] {
def createNewVersion(entity:E): E
}
object VersionGenerator {
implicit def persistableEntityVersionGenerator[E <: PersistableEntity[Int]: ClassTag]: VersionGenerator[E] =
new VersionGenerator[E] {
type CC = E {def copy(version: Int): E}
override def createNewVersion(persistableEntity: E): E = persistableEntity match {
case caseClassInstance: CC =>
import scala.language.reflectiveCalls
caseClassInstance.copy(version = persistableEntity.version+1)
case _ => persistableEntity
}
}
}
这些工作正常,因为我能够验证它
val alphaVersionGenerator = implicitly[VersionGenerator[Alpha]]
val betaVersionGenerator = implicitly[VersionGenerator[Beta]]
val gammaVersionGenerator = implicitly[VersionGenerator[Gamma]]
// Output
alphaVersionGenerator: VersionGenerator[Alpha] = com.example.A$A107$A$A107$VersionGenerator$$anon@22be8030
betaVersionGenerator: VersionGenerator[Beta] = com.example.A$A107$A$A107$VersionGenerator$$anon@309664a7
gammaVersionGenerator: VersionGenerator[Gamma] = com.example.A$A107$A$A107$VersionGenerator$$anon@c1464bc
到目前为止一切顺利。我还创建了一个隐式 class 并如下所示验证它
implicit class VersionGenerationOps[E <:PersistableEntity[_]](persistableEntity: E)(implicit evidence: VersionGenerator[E]) {
def newVersion: E = evidence.createNewVersion(persistableEntity)
}
val updatedAlpha = alpha.newVersion
这是输出
updatedAlpha: Alpha = Alpha(2)
同样,下面的也可以
def createNewVersions[E <: PersistableEntity[_]](input:E*)(implicit versionGenerator: VersionGenerator[E]): Seq[E] = {
input.map(versionGenerator.createNewVersion)
}
createNewVersions(beta1, beta2, gamma1, gamma2, gamma3, gamma4, gamma5)
// Output
res0: Seq[PersistableEntity[Int] with Serializable] = ArrayBuffer(Beta(101), Beta(201), Gamma(1001), Gamma(1101), Gamma(1201), Gamma(1301), Gamma(1401))
现在是依赖树结构的表示
// ADT
sealed trait DependencyTree[+E <:PersistableEntity[_]]
case class Branch[+E <:PersistableEntity[_]](value: E, incoming: Set[_<:DependencyTree[_<:PersistableEntity[_]]]) extends DependencyTree[E]
case class Leaf[+E <:PersistableEntity[_]](value: E) extends DependencyTree[E]
以及树形结构中表示的实体之间的关系
val myDependencyTree = Branch(alpha, Set(Leaf(gamma1), Leaf(gamma2),
Branch(beta1, Set(Leaf(gamma3), Leaf(gamma4))),
Branch(beta2, Set(Leaf(gamma5)))
))
树的简单遍历工作正常,如下所示
// Simple traversal
def processDependencyTree[E <: PersistableEntity[_]](input: DependencyTree[E]): Stream[_<: PersistableEntity[_]] = {
def processDependencyTreeBFS[T <: PersistableEntity[_]](accumulator: Stream[_ <: PersistableEntity[_]],
dependencyTree: DependencyTree[T]): Stream[_<: PersistableEntity[_]] = {
import VersionGenerator._
dependencyTree match {
case node@Leaf(entity) =>
println(s"Processing leaf: $entity")
Stream(entity)
case node@Branch(entity, dependencies) =>
println(s"Processing branch: $entity")
val processedChildren:Stream[_<: PersistableEntity[_]] = dependencies
.toSeq
.map( (dt: DependencyTree[_ <: PersistableEntity[_]]) => processDependencyTreeBFS(accumulator, dt) )
.reduce((first, second) => first ++ second)
Stream.cons(entity, processedChildren)
}
}
processDependencyTreeBFS(Stream.empty, input)
}
val result = processDependencyTree(myDependencyTree).toList
// Output
Processing branch: Alpha(1)
Processing leaf: Gamma(1000)
Processing leaf: Gamma(1100)
Processing branch: Beta(100)
Processing leaf: Gamma(1200)
Processing leaf: Gamma(1300)
Processing branch: Beta(200)
Processing leaf: Gamma(1400)
result: List[PersistableEntity[_]] = List(Alpha(1), Gamma(1000), Gamma(1100), Beta(100), Gamma(1200), Gamma(1300), Beta(200), Gamma(1400))
好了,问题来了。如果我引入类型为 class 实例的隐式参数,隐式解析会因类型擦除而失败。
这是失败的更新代码
import scala.reflect.runtime.universe._
def processDependencyTree[E <: PersistableEntity[_]](input: DependencyTree[E])
(implicit versionGenerator: VersionGenerator[E], weakTypeTag: WeakTypeTag[E]): Stream[_<: PersistableEntity[_]] = {
def processDependencyTreeBFS[T <: PersistableEntity[_]](accumulator: Stream[_ <: PersistableEntity[_]],
dependencyTree: DependencyTree[T])
(implicit versionGenerator: VersionGenerator[T], weakTypeTag: WeakTypeTag[T]): Stream[_<: PersistableEntity[_]] = {
import VersionGenerator._
dependencyTree match {
case node@Leaf(entity) =>
println(s"Processing leaf: $entity")
Stream(versionGenerator.createNewVersion(entity))
case node@Branch(entity, dependencies) =>
println(s"Processing branch: $entity")
val processedChildren:Stream[_<: PersistableEntity[_]] = dependencies
.toSeq
.map( (dt: DependencyTree[_ <: PersistableEntity[_]]) => processDependencyTreeBFS(accumulator, dt) )
.reduce((first, second) => first ++ second)
Stream.cons(versionGenerator.createNewVersion(entity), processedChildren)
}
}
processDependencyTreeBFS(Stream.empty, input)
}
val result = processDependencyTree(myDependencyTree).toList
这里是错误
Error:(84, 88) could not find implicit value for parameter versionGenerator: A$A189.this.VersionGenerator[_]
.map( (dt: DependencyTree[_ <: PersistableEntity[_]]) => processDependencyTreeBFS(accumulator, dt) )
Error:(84, 88) not enough arguments for method processDependencyTreeBFS: (implicit versionGenerator: A$A189.this.VersionGenerator[_], implicit weakTypeTag: reflect.runtime.universe.WeakTypeTag[_])Stream[_ <: A$A189.this.PersistableEntity[_]].
Unspecified value parameters versionGenerator, weakTypeTag.
.map( (dt: DependencyTree[_ <: PersistableEntity[_]]) => processDependencyTreeBFS(accumulator, dt) )
Error:(194, 88) could not find implicit value for parameter versionGenerator: inst$A$A.VersionGenerator[_]
.map( (dt: DependencyTree[_ <: PersistableEntity[_]]) => processDependencyTreeBFS(accumulator, dt) )
Error:(194, 88) not enough arguments for method processDependencyTreeBFS: (implicit versionGenerator: inst$A$A.VersionGenerator[_], implicit weakTypeTag: reflect.runtime.universe.WeakTypeTag[_])Stream[_ <: inst$A$A.PersistableEntity[_]].
Unspecified value parameters versionGenerator, weakTypeTag.
.map( (dt: DependencyTree[_ <: PersistableEntity[_]]) => processDependencyTreeBFS(accumulator, dt) )
任何修复隐式解析的帮助将不胜感激。非常感谢您的时间和努力!
我找到了答案。由于类型 class 仅针对特定类型声明,即
E <: PersistableEntity[Int]
解析成功的隐式参数,也必须匹配,不能使用
T <: PersistableEntity[_]