Scala: преодолеть стирание универсального типа для неявного разрешения - PullRequest
0 голосов
/ 07 февраля 2019

Позвольте мне сначала объяснить мой вариант использования.У меня есть набор сущностей в хранилище данных, которые связаны друг с другом в древовидной структуре.Когда номер версии корневого узла в древовидной структуре увеличивается, номера версий связанных узлов должны быть увеличены.

Вот набор сущностей

// 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 голосов
/ 07 февраля 2019

Я нашел ответ.Поскольку класс типа был объявлен только для определенного типа, т.е.

E <: PersistableEntity[Int]

Неявный параметр для успешного разрешения, также должен соответствовать этому и не использовать

T <: PersistableEntity[_]
...