代码之家  ›  专栏  ›  技术社区  ›  Rex Kerr

Scala中具有继承返回类型的集合的最小框架

  •  15
  • Rex Kerr  · 技术社区  · 14 年前

    假设有人想建立一个新的泛型类, Novel[A]

    class Novel[A] /* What goes here? */ {
      /* Must you have stuff here? */
      def reverse/* What goes here instead of :Novel[A]? */ = //...
      def revrev/*?*/ = reverse.reverse
    }
    class ShortStory[A] extends Novel[A] /* What goes here? */ {
      override def reverse: /*?*/ = //...
    }
    val ss = new ShortStory[String]
    val ss2 = ss.revrev  // Type had better be ShortStory[String], not Novel[String]
    

    如果你愿意的话,这个最小的数目会改变吗 Novel 是协变的吗?

    (2.8集合除其他功能外还实现了这一点,但它们还以更奇特(更有用)的方式处理返回类型——问题是,如果只希望使用此子类型始终返回子类型功能,那么框架可以逃脱的风险有多小。)

    编辑:在上面的代码中假设 reverse this.type ,但这不起作用,因为副本不是 this

    Arjan联系到另一个问题,提出了以下解决方案:

    def reverse: this.type = {
      /*creation of new object*/.asInstanceOf[this.type]
    }
    

    得到一个 ShortStory 当我们认为我们需要的时候(例如,我们不必重写 颠倒

    3 回复  |  直到 11 年前
        1
  •  3
  •   retronym    14 年前

    我还没想清楚,但它会检查:

    object invariant {
      trait Novel[A] {
        type Repr[X] <: Novel[X]
    
        def reverse: Repr[A]
    
        def revrev: Repr[A]#Repr[A]
           = reverse.reverse
      }
      class ShortStory[A] extends Novel[A] {
        type Repr[X] = ShortStory[X]
    
        def reverse = this
      }
    
      val ss = new ShortStory[String]
      val ss2: ShortStory[String] = ss.revrev
    }
    
    object covariant {
      trait Novel[+A] {
        type Repr[X] <: Novel[_ <: X]
    
        def reverse: Repr[_ <: A]
    
        def revrev: Repr[_ <: A]#Repr[_ <: A] = reverse.reverse
      }
    
      class ShortStory[+A] extends Novel[A] {
        type Repr[X] = ShortStory[X]
    
        def reverse = this
      }
    
      val ss = new ShortStory[String]
      val ss2: ShortStory[String] = ss.revrev
    }
    

    共变型版本可以更好:

    object covariant2 {
      trait Novel[+A] {
        type Repr[+X] <: Novel[X]
    
        def reverse: Repr[A]
    
        def revrev: Repr[A]#Repr[A] = reverse.reverse
      }
    
      class ShortStory[+A] extends Novel[A] {
        type Repr[+X] = ShortStory[X]
    
        def reverse = this
      }
    
      val ss = new ShortStory[String]
      val ss2: ShortStory[String] = ss.revrev
    }
    
        2
  •  5
  •   Arjan Blokzijl    14 年前

    编辑:我刚刚意识到雷克斯在他的例子中有一个具体的阶级小说,而不是我在下面使用的特质。因此,trait实现有点过于简单,无法解决Rex的问题。使用一个具体的类(见下文)也可以做到这一点,但我唯一能做到这一点的方法是通过一些强制转换,这使得这不是真正的“编译时类型安全”。因此,这不符合解决方案的条件。

    也许不是最漂亮的,但是使用抽象成员类型的简单示例可以实现如下:

    
    trait Novel[A] { 
       type T <: Novel[A] 
       def reverse : T 
       def revrev : T#T = reverse.reverse 
    }
    
    class ShortStory[A](var story: String) extends Novel[A] {
     type T = ShortStory[A]
     def reverse : T = new ShortStory[A](story reverse)
     def myMethod: Unit = println("a short story method")
    }
    
    scala> val ss1 = new ShortStory[String]("the story so far")
    ss1: ShortStory[String] = ShortStory@5debf305
    
    scala> val ssRev = ss1 reverse 
    ssRev: ss1.T = ShortStory@5ae9581b
    
    scala> ssRev story
    res0: String = raf os yrots eht
    
    scala> val ssRevRev = ss1 revrev
    ssRevRev: ss1.T#T = ShortStory@2429de03
    
    scala> ssRevRev story
    res1: String = the story so far
    
    scala> ssRevRev myMethod
    a short story method
    

    然而,对于给定的情况,它似乎起到了作用。

    还有一个编辑:也可以使用具体的类来完成类似的操作,但这也不足以保证类型安全:

    
    class Novel[A](var story: String) {
      type T <: Novel[A] 
      def reverse: T = new Novel[A](story reverse).asInstanceOf[T]  
      def revrev : T#T = reverse.reverse
    }
    class ShortStory[A](var s: String) extends Novel[A](s) {
     type T = ShortStory[A]
     override def reverse : T = new ShortStory(story reverse)
     def myMethod: Unit = println("a short story method")
    }
    

    代码将与trait示例中的一样工作。但它也面临着雷克斯在编辑中提到的同样的问题。要使此编译,不需要重写ShortStory。但是,如果不这样做并在一个简短的实例上调用reverse方法,它将在运行时失败。

        3
  •  2
  •   Rex Kerr    14 年前

    在讨论了Scala邮件列表之后——非常感谢那里的人让我走上正轨--我认为这是最接近于一个最小的框架。我把它放在这里作为参考,我使用了一个不同的例子,因为它突出了正在发生的更好的事情:

    abstract class Peano[A,MyType <: Peano[A,MyType]](a: A, f: A=>A) {
      self: MyType =>
      def newPeano(a: A, f: A=>A): MyType
      def succ: MyType = newPeano(f(a),f)
      def count(n: Int): MyType = {
        if (n<1) this
        else if (n==1) succ
        else count(n-1).succ
      }
      def value = a
    }
    
    abstract class Peano2[A,MyType <: Peano2[A,MyType]](a: A, f: A=>A, g: A=>A) extends Peano[A,MyType](a,f) {
      self: MyType =>
      def newPeano2(a: A, f: A=>A, g: A=>A): MyType
      def newPeano(a: A, f: A=>A): MyType = newPeano2(a,f,g)
      def pred: MyType = newPeano2(g(a),f,g)
      def uncount(n: Int): MyType = {
        if (n < 1) this
        else if (n==1) pred
        else uncount(n-1).pred
      }
    }
    

    这里的关键是 MyType 类型参数,它是我们最终将使用的类类型的占位符。每次继承时,我们都必须将其重新定义为类型参数,并添加一个构造函数方法,该方法将创建此类型的新对象。如果构造函数发生变化,我们必须创建一个新的构造函数方法。

    现在,当您想创建一个实际使用的类时,您只需在构造函数方法中填充对new的调用(并告诉类它是自己的类型):

    class Peano2Impl[A](a: A, f: A=>A, g: A=>A) extends Peano2[A,Peano2Impl[A]](a,f,g) {
      def newPeano2(a: A, f: A=>A, g: A=>A) = new Peano2Impl[A](a,f,g)
    }
    

    然后你就跑了:

    val p = new Peano2Impl(0L , (x:Long)=>x+1 , (y:Long)=>x-1)
    
    scala> p.succ.value
    res0: Long = 1
    
    scala> p.pred.value
    res1: Long = -1
    
    scala> p.count(15).uncount(7).value
    res2: Long = 8
    

    我的类型

    这种策略对于数据中的协方差非常有效 A 另外,除了这个特殊的例子不起作用,因为 f g 不是协变的。