Scala:克服泛型类型擦除以实现隐式解析

时间:2019-02-06 21:40:15

标签: scala generics implicit type-erasure

让我先解释一下我的用例。我在数据存储区中有一组实体,它们在树结构中相互关联。当增加树形结构中根节点的版本号时,也必须增加相关节点的版本号。

这是实体集

// 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)

我创建了一个类型类来容纳增加版本号的逻辑。如下图所示

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$1@22be8030
betaVersionGenerator: VersionGenerator[Beta] = com.example.A$A107$A$A107$VersionGenerator$$anon$1@309664a7
gammaVersionGenerator: VersionGenerator[Gamma] = com.example.A$A107$A$A107$VersionGenerator$$anon$1@c1464bc

到目前为止,一切都很好。我还创建了一个隐式类,并按如下所示进行了验证

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))

好的,现在出现了问题。如果我引入类型类实例的隐式参数,则由于类型擦除,隐式解析将失败。

这是失败的更新代码

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[_$20]
                    .map( (dt: DependencyTree[_ <: PersistableEntity[_]]) => processDependencyTreeBFS(accumulator, dt) )


Error:(84, 88) not enough arguments for method processDependencyTreeBFS: (implicit versionGenerator: A$A189.this.VersionGenerator[_$20], implicit weakTypeTag: reflect.runtime.universe.WeakTypeTag[_$20])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[_$33]
                    .map( (dt: DependencyTree[_ <: PersistableEntity[_]]) => processDependencyTreeBFS(accumulator, dt) )

Error:(194, 88) not enough arguments for method processDependencyTreeBFS: (implicit versionGenerator: inst$A$A.VersionGenerator[_$33], implicit weakTypeTag: reflect.runtime.universe.WeakTypeTag[_$33])Stream[_ <: inst$A$A.PersistableEntity[_]].
Unspecified value parameters versionGenerator, weakTypeTag.
                    .map( (dt: DependencyTree[_ <: PersistableEntity[_]]) => processDependencyTreeBFS(accumulator, dt) )

对解决隐式分辨率的任何帮助将不胜感激。非常感谢您的时间和精力!

1 个答案:

答案 0 :(得分:0)

我找到了答案。由于类型类仅针对特定类型声明,即

E <: PersistableEntity[Int]

要成功解决的隐式参数,还必须与之匹配且不使用

T <: PersistableEntity[_]