0

我是 Scala 的新手。我知道类型理论的基础。

问题是使用回调创建节点的图形对象。

以下是当前实现的简化版本。这是图书馆的一部分。

它有效,但充满Any. 我认为摆脱 all 是不可能的Any,但我怀疑有更好的方法。

特别是,CallbackNode接受协变类型参数,但回调的类型是逆变的,则变为Any.
但是回调在用户代码中无处不在,因此最好有类型A以提高可用性。

让我知道您将如何针对此类问题进行设计。

trait Graph[N] {
  var nodes: List[N]
  var edges: List[Edge[N]]

  def register(node: N) { nodes = node :: nodes }
  def dependants(node: N): Seq[N] = { ... }
}

object MyGraph with Graph[CallbackNode[Any]] {
  ...
}

class CallbackNode[+A](getter: => A) {
  var cache: Option[Any]
  MyGraph.register(this)

  def onChange(callback: Any => Unit) { ... }
  def update() {
    val v = getter
    storeInCache(v)
    dependants.foreach { n => n.notify() }
    callbacks.foreach { c => c(v) }
  }

  ...
}
4

2 回答 2

0

如果您可以接受每个给定图形实例的所有回调都具有相同的类型 A,则可以将类型作为图形定义的一部分移动。既然您可能不想要,那么您需要使用第一个共同祖先,这可能最终是 Any。

然而,在 Scala 中,即使它不是微不足道的,也有一种方法可以让数据结构表现得像集合,但保留你想要的类型。你应该看看@MilesSabin shapeless library 来了解它是如何完成的。 https://github.com/milessabin/shapeless

一旦掌握了 HList 和 HMap 等 Shapeless 概念,就可以开发 HGraph 并将其开源 :)

于 2013-06-24T11:34:48.603 回答
0

感谢@senia 的评论,我找到了方法。

在这种情况下,callbackList并且cache可以是private[this](实例私有。它对同一类的其他实例是不可见的)。然后我使方法中的类型保持不变。

class CallbackNode[+A](getter: => A) {
  private[this] var cache: Option[A]
  private[this] var callbackList: List[A => Unit]
  MyGraph.register(this)

  def onChange(callback: A => Unit) { ... }
  def update() {
    val v = getter
    storeInCache(v)
    dependants.foreach { n => n.notify() }
    callbacks.foreach { c => c(v) }
  }

  ...
}

图仍然CallbackNode[Any]是节点。 MyGraph是供内部使用的,所以这Any不是一个大问题。如果需要,来自 shapeless 的想法也将解决这个问题。

于 2013-06-24T12:35:43.203 回答