3

我正在尝试实现一个结果缓存(Loader类),它将类的可散列实例映射Resolver[T]Result实例,其中Tis 的类型为Result. 当然,用户可以专门化Result提供特定于应用程序的额外方法的类。关键是使用该Loader.get方法用户还必须指定结果类型:

    val s = Loader.get(new SpecializedResolver()).asInstanceOf[SpecializedResult]

我想要的是避免强制转换并使其隐式,所以代码应该是这样的:

    val implicitS = Loader.get(new SpecializedResolver())

我目前拥有的代码如下:

import scala.language.existentials

abstract class Result {
  def computed: Boolean
}

abstract class Resolver[T <: Result] {
    def result(): T forSome {type T <: Result}
}

class SpecializedResult extends Result {
  def computed = true
  def special() = 42
}

class SpecializedResolver extends Resolver[SpecializedResult] {
  def result = new SpecializedResult
}

object Loader {
  val cache = collection.mutable.Map[K forSome {type K <: Resolver[_]}, V forSome {type V <: Result}]()

  def get[K <: Resolver[_]](k: K): V forSome {type V <: Result} = {
    cache.getOrElseUpdate(k, { k.result } )
  }
}

object Runner {
  def main(args: Array[String]) {
    val s = Loader.get(new SpecializedResolver()).asInstanceOf[SpecializedResult]
    println("Value is: %d".format(s.special))

    val implicitS = Loader.get(new SpecializedResolver())
    // test.scala:34: error: value special is not a member of Result
    println("Value is: %d".format(implicitS.special))
  }
}

由于我对 scala 语言比较陌生,我的问题是:是否有可能通过使用 scala 提供的高级类型功能来实现这种隐式转换?我看了一下演讲“Scala Land of the Land of High Wizardry”,演讲者 Daniel Spiewak 介绍了一个执行类似操作的高阶HOMap,但我仍然不太了解如何使他的解决方案适应我的情况。

4

2 回答 2

3

首先,你不需要这里的所有存在主义。在某些情况下,它们只是过于冗长——例如,<code>K forSome { type K <: Resolver[_] } 与K <: Resolver[_]. 在其他情况下,它们会引入看似无意的行为。例如:

class FooResult extends Result { def computed = true }
class BarResult extends Result { def computed = true }

object Foo extends Resolver[FooResult] { def result() = new BarResult }

这编译得很好,因为T在返回类型中result隐藏了类的类型参数T

接下来,你在这里考虑过惰性值吗?他们会为你处理这种缓存——你只需在你的实现中定义resultas并且结果将只计算一次(当它第一次需要时)。lazy val result = ...Resolver

如果您决定需要手动处理此缓存,则在这种情况下,您可以证明用强制转换欺负编译器是合理的。例如,给定以下设置:

trait Result { def computed: Boolean }
trait Resolver[T <: Result] { def result(): T }

我们可以写:

object Loader {
  private[this] val cache = collection.mutable.Map.empty[Resolver[_], Result]

  def get[V <: Result](k: Resolver[V]): V =
    cache.getOrElseUpdate(k, k.result()).asInstanceOf[V]

}

我们知道类型会起作用,因为对进入地图只有一种方式。

更新:我Loader在这里的解决方案与 Sergey 的解决方案基本相同(请注意,锁定地图是个好主意,并且您不需要在调用周围使用括号result,因为 of 的第二个参数getOrElseUpdate是按名称)。无论如何我都会留下我的答案,因为前半部分是重要的部分。

于 2013-08-17T14:06:51.480 回答
2

在您的情况下,您可以使用简单的解决方案(仅更改代码):

abstract class Resolver[T <: Result] {
    def result(): T
}
object Loader {
  val cache = collection.mutable.Map[Resolver[_], Any] ()

  def get[K <: Result](k: Resolver[K]): K = {
    cache.getOrElseUpdate(k, { k.result } ).asInstanceOf[K]
  }
}
于 2013-08-17T13:56:27.683 回答