【问题标题】:Implementing covariance using Virtual types (Abstract types)使用虚拟类型(抽象类型)实现协方差
【发布时间】:2013-05-21 01:35:34
【问题描述】:

为了保证协方差,有三种方法:

  1. 纯协方差:使用埃菲尔语言,
  2. 模拟协方差:使用强制转换和重载
  3. 使用 F 有界多态或虚类型

所以我正在使用虚拟类型测试解决方案,在以下示例中使用 scala 抽象类型:

  • 我们定义了 3 个抽象类:Graph、Node、Edge
  • Graph 类定义了 2 个方法:attachNode(Node) 和 detachNode(Node)
  • Node 类定义了 2 个方法:attachToGraph(Graph) 和 detachFromGraph()

使用继承我们将为不同的域创建不同的子类:

  • 对于网络:class Network extends Graph 和 class Host extends Node,..
  • 对于化学:class Molecule extends Graph 和 class Atom extends Node,..
  • ...

唯一的限制是通过例如将 Atom 附加到网络来避免创建“嵌合体”。所以模型比较简单:

abstract class Graph {
  type CompatibleNode <: Node
  protected var myNodes = new ArrayBuffer[CompatibleNode]()

  def attachNode(node : compatibleNode) = {
      .... 

      // Inform the node about the attachement so it can do update
      node.attachToGraph(this)

      // save the node
      myNodes += node
  }
}

abstract class Node {
  type CompatibleGraph >: scala.Null <: Graph
  protected var myGraph : CompatibleGraph = null

  def attachToGraph(graph : compatibleGraph) = {
      .... 

      // Inform the graph about the attachement so it can do update
      graph.attachNode(this)

      // save the node
      myGraph = graph
  }
}

在创建特殊图表之后,我们只需覆盖虚拟类型:

class Network extends Graph { override type CompatibleNode = Host}
class Host extends Node { override type CompatibleGraph = Network}

class Molecule extends Graph { override type CompatibleNode = Atom}
class Atom extends Node { override type CompatibleGraph = Molecule}

这应该很好用(它适用于 NIT 语言)但我有不同的错误:

  • 首先,调用graph.attachNode(this)时输入不匹配,这需要graph.CompatibleNode,发现:Graph,所以我投了这个:

    graph.attachNode(this.asInstanceOf[graph.CompatibleNode])

请注意,NIT 语言会进行隐式转换。

  • 其次,对于 detachFromGraph() 方法:

    类节点{

    ...
    
    def detachFromGraph() = {
    
         ....
    
         // inform my graph 
         myGraph.detachNode(this.asInstanceOf[myGraph.CompatibleNode])
    
         ...
     }
    

    }

我收到错误:myGraph.compatibleNode : requiered stable identifier,在搜索和阅读规范后,我发现: -> 一个稳定的标识符是以标识符结尾的路径 -> p.x 是路径,如果p 是路径并且x 是稳定成员 -> 稳定成员是.....或非易失类型的值定义 -> volatile types:类型参数或抽象类型,....

简而言之,我不能在 path 中使用抽象类型的对象,为什么?我不知道!

所以如果有人有建议,或者即使可以使用 scala 抽象类型作为虚拟类型。

【问题讨论】:

  • 我不明白 Eiffel 如何“确保”协方差。它处理协方差的方式甚至违反了 Liskov 替换原则,允许协变类型参数,即使它们处于逆变位置。
  • 我在 this question 的 cmets 中读到(我有类似的问题),可以在 C++ 中实现其中的一些想法。有人知道怎么做吗?
  • 你在说什么样的协方差?如果你能说出你的定义,也许会有所帮助。目前你的定义是不健全的,因为如果你调用attachNode,你不能确保this与Node兼容(和v.v.为attachToGraph),因为你的类型方案不需要/表达这种兼容性关系是对称的。

标签: scala covariance abstract-type


【解决方案1】:

这有效(并且也消除了无限循环)。该包只是为了能够说明包私有方法。

package foo {
  abstract class Graph {
    type CompatibleNode <: Node
    protected var myNodes = new ArrayBuffer[CompatibleNode]()

    def attachNode(node: CompatibleNode)
                  (implicit ev: this.type <:< node.CompatibleGraph) {

      // Inform the node about the attachement so it can do update
      node.backAttach(ev(this))

      // save the node
      myNodes += node
    }

    private[foo] def backAttach(node: CompatibleNode) { myNodes += node }
  }

  abstract class Node {
    type CompatibleGraph >: scala.Null <: Graph
    protected var myGraph: CompatibleGraph = null

    def attachToGraph(graph: CompatibleGraph)
                     (implicit ev: this.type <:< graph.CompatibleNode) {

      // Inform the graph about the attachement so it can do update
      graph.backAttach(ev(this))

      // save the node
      myGraph = graph
    }

    private[foo] def backAttach(graph: CompatibleGraph) { myGraph = graph }
  }
}

现在:

class Network extends foo.Graph { override type CompatibleNode = Host}
class Host extends foo.Node { override type CompatibleGraph = Network}

class Molecule extends foo.Graph { override type CompatibleNode = Atom}
class Atom extends foo.Node { override type CompatibleGraph = Molecule}

并尝试一下:

object GraphTest {
  val n = new Network()
  val h = new Host()

  n.attachNode(h)

  val a = new Atom()

  n.attachNode(a) // fails: type mismatch;
  // found : Atom required: GraphTest.n.CompatibleNode (which expands to) Host
}

【讨论】:

    【解决方案2】:

    这里有一个解决方法:

      def detachFromGraph () {
          detachFromThisGraph (myGraph)
      }
    
      def detachFromThisGraph (graph : CompatibleGraph) {
          graph.detachNode(this.asInstanceOf[graph.CompatibleNode])
      }
    

    我一点也不知道什么是稳定标识符,也不知道为什么需要它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-02-02
      • 2020-04-21
      • 2010-11-22
      • 1970-01-01
      • 2021-01-31
      • 2020-11-19
      • 2013-01-11
      相关资源
      最近更新 更多