【问题标题】:self annotation using abstract type使用抽象类型的自我注释
【发布时间】:2012-07-19 01:53:15
【问题描述】:

我想定义一个抽象类型的抽象递归数据结构。 像这样:

case class ParentA( name : String, children : List[ParentA] ) extends Parent {
    type PARENT = ParentA
}

case class ParentB( name : String, children : List[ParentB] ) extends Parent {
    type PARENT = ParentB
}

sealed abstract class Parent {
    // we'd like to Define Parent as a PARENT
    // something like: 
    // this : PARENT =>

    type PARENT <: Parent

    val name : String

    val children : List[PARENT]

    def findParent(name:String) : Option[PARENT] = {
        if( name == this.name ) {
            Some(this) // ouch
        } else {
            // re-ouch
            children.flatMap( f => f.findParent(name) )
        }
    }
}

val a2a : ParentA = ParentA("a",List(ParentA("a1",Nil),ParentA("a2",List(ParentA("a2a",Nil))))).findParent("a2a").get

当然这不会编译,因为编译器无法猜测 Parent.this 是 PARENT 。

error: type mismatch;
found   : Parent.this.type (with underlying type this.Parent)
required: Parent.this.PARENT
        Some(this)

还有

error: type mismatch;
found   : List[Parent.this.PARENT#PARENT]
required: Option[Parent.this.PARENT]
        children.flatMap( f => f.findParent(name) )

我可以通过在这里和那里进行转换来解决它,但最好能够告诉编译器 Parent 是 PARENT。 或者也许我错过了什么:)

编辑 - 泛型

我忘了提到泛型不是一个选项。这个例子实际上是一个更微妙问题的简化版本。使用泛型将导致程序的二次增长。这是一个链接,解释了为什么泛型并不总是可行的替代方案:Scala: Abstract types vs generics

基本上我最好使用抽象类型 - 甚至既不使用抽象类型也不使用泛型 - 和强制转换。

【问题讨论】:

    标签: scala abstract-type


    【解决方案1】:

    与@Debilski 提到的想法相同,但没有额外的类型参数:

    case class ParentA(name: String, children: List[ParentA]) extends Parent[ParentA]
    
    case class ParentB(name: String, children: List[ParentB]) extends Parent[ParentB]
    
    sealed abstract class Parent[P <: Parent[P]] { this: P =>
    
      def name: String
    
      def children: List[P]
    
      def findParent(name: String): Option[P] =
        if (name == this.name) Some(this)
        else children.flatMap(_.findParent(name)).headOption
    }
    

    顺便说一下,抽象成员使用defs 而不是vals。它们在子类中实现时具有更大的灵活性。

    【讨论】:

    • 我忘记了 P &lt;: Parent[P] 把戏。难怪它没有像我认为的那样 100% 正常工作。
    • 对不起,我忘了提到使用泛型不是一种选择。我将编辑我的问题以解释原因。
    【解决方案2】:

    仅供参考。 sschaef 有更好的方法。

    这使用 Scala CRTP 和自身类型进行编译。

    case class ParentA(name : String, children : List[ParentA]) extends Parent[ParentA]
    
    case class ParentB(name : String, children : List[ParentB]) extends Parent[ParentB]
    
    sealed abstract class Parent[T] { this : T =>
        type PARENT = Parent[T] with T
    
        val name : String
    
        val children : List[PARENT]
    
        def findParent(name:String) : Option[PARENT] = {
            if( name == this.name ) {
                Some(this)
            } else {
                children.flatMap( f => f.findParent(name) ).headOption
            }
        }
    }
    val a2a : ParentA = ParentA("a",List(ParentA("a1",Nil),ParentA("a2",List(ParentA("a2a",Nil))))).findParent("a2a").get
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-24
      • 1970-01-01
      • 2017-08-14
      • 2014-02-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-01
      相关资源
      最近更新 更多