Scala 使用虚拟类型(抽象类型)实现协方差

Scala 使用虚拟类型(抽象类型)实现协方差,scala,covariance,abstract-type,Scala,Covariance,Abstract Type,要确保协方差,有三种方法: 纯协方差:使用埃菲尔语言 模拟协方差:使用强制转换和重载 使用F-有界多态射或虚类型 因此,我正在使用虚拟类型测试解决方案,在以下示例中使用scala抽象类型: 我们定义了3个抽象类:图、节点、边 Graph类定义了两种方法:attachNode(节点)和detachNode(节点) Node类定义了2个方法:attachToGraph(Graph)和detachFromGraph() 使用Inhereness,我们将为不同的域创建不同的子类: 对于网络:类网络

要确保协方差,有三种方法:

  • 纯协方差:使用埃菲尔语言
  • 模拟协方差:使用强制转换和重载
  • 使用F-有界多态射或虚类型
  • 因此,我正在使用虚拟类型测试解决方案,在以下示例中使用scala抽象类型:

    • 我们定义了3个抽象类:图、节点、边
    • Graph类定义了两种方法:attachNode(节点)和detachNode(节点)
    • Node类定义了2个方法:attachToGraph(Graph)和detachFromGraph()
    使用Inhereness,我们将为不同的域创建不同的子类:

    • 对于网络:
      类网络扩展图形
      ,而
      类主机扩展节点
    • 对于化学:
      类分子扩展图形
      类原子扩展节点
    唯一的限制是通过例如将原子连接到网络来避免创建“嵌合体”。因此,模型相对简单:

    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
      }
    }
    
    这应该可以很好地工作(在NIT语言中工作),但我有不同的错误:

    • 首先,调用
      graph.attachNode(this)
      this required
      graph.CompatibleNode
      ,发现类型不匹配:
      graph
      ,因此我强制转换了以下内容:

      attachNode(这个.asInstanceOf[graph.CompatibleNode])

    请注意,NIT语言不会隐式强制转换

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

      类节点{

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

    我得到了错误:
    myGraph.compatibleNode:required稳定标识符
    ,在搜索和阅读规范后,我发现: ->稳定标识符是以标识符结尾的路径 ->如果
    p
    是路径且
    x
    是稳定成员,则
    p.x
    是路径 ->稳定的成员是。。。。。或非易失性类型的值定义 ->易失性类型:类型参数或抽象类型

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

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

    这里有一个解决方法:

      def detachFromGraph () {
          detachFromThisGraph (myGraph)
      }
    
      def detachFromThisGraph (graph : CompatibleGraph) {
          graph.detachNode(this.asInstanceOf[graph.CompatibleNode])
      }
    
    我一点也不知道什么是稳定的标识符,或者为什么这里需要它。

    这里有一个解决方法:

      def detachFromGraph () {
          detachFromThisGraph (myGraph)
      }
    
      def detachFromThisGraph (graph : CompatibleGraph) {
          graph.detachNode(this.asInstanceOf[graph.CompatibleNode])
      }
    
    我一点也不知道什么是稳定的标识符,也不知道为什么这里需要它。

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

    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 }
      }
    }
    
    并尝试一下:

    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
    }
    
    这是可行的(并且消除了无限循环)。包只是为了能够说明包私有方法

    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 }
      }
    }
    
    并尝试一下:

    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
    }
    

    我不明白埃菲尔如何“确保”协方差。它的处理协方差的方式甚至违反了LISKOV替换原理,即使允许它们在逆变位置,也允许协变类型参数。我读到了评论(我有类似的问题),可以在C++中实现这些想法。有人知道怎么做吗?你说的是什么样的协方差?如果你能说出你的定义也许会有帮助。目前,您的定义是不合理的,因为如果调用
    attachNode
    ,您无法确保
    节点
    兼容(对于
    attachToGraph
    ),因为您的类型方案不要求/表示兼容关系是对称的。我看不出埃菲尔如何“确保”协方差。它的处理协方差的方式甚至违反了LISKOV替换原理,即使允许它们在逆变位置,也允许协变类型参数。我读到了评论(我有类似的问题),可以在C++中实现这些想法。有人知道怎么做吗?你说的是什么样的协方差?如果你能说出你的定义也许会有帮助。目前,您的定义不合理,因为如果调用
    attachNode
    ,您无法确保
    节点
    兼容(对于
    attachToGraph
    ),因为您的类型方案不要求/表示兼容关系是对称的。