【问题标题】:How to use scala's singleton-object types to require that an object was created by one of its constructor parameters?如何使用 scala 的单例对象类型来要求对象是由其构造函数参数之一创建的?
【发布时间】:2012-10-21 23:28:37
【问题描述】:

我有一个检索项目的访问器类。它还可以将 Item 作为参数并从数据库中返回该 Item 的最新版本。当它创建一个 Item 时,它会将自己作为参数传递给 Item。

我希望编译器静态地要求 Accessor 实例只接受它自己创建的项目。 How to use Scala's singleton-object types? 涵盖了这一点,但是我还希望 Item 实例能够将自身作为参数传递给它自己的 Accessor 以检索其自身的最新版本。

这样做的困难在于Item类定义中的类型参数像这样

class Item[A <: Accessor](acc: A)

不能引用 acc 本身的类型。从项目acc.type &lt;: A &lt;: Accessor 的角度来看,项目中的thisItem[A],而不是Item[acc.type]。因此这不起作用:

class Item[A <: Accessor](acc: A) {
    acc.accept(this) // Type Mismatch: found Item[A], required Item[Item.this.acc.type]
}

class Accessor {
    def make() = new Item[this.type](this)
    def accept(item: Item[this.type]) = "accepted"
}

然后我尝试了这个:

object A1 extends Accessor[A1.type](A1) // illegal cyclic reference involving object A1

class Item[+A <: Accessor[A]](acc: A) {
    acc.accept(this)
    A1.accept(this) // Compile error (good)
}

class Accessor[+A <: Accessor[A]](me: => A) {
    def make = new Item[A](me)
    def accept(item: Item[A]) = "accepted"
}

问题实际上出在创建 Accessor 的实例。

我在上面尝试了一种变体,结果证明这是相同基本困境的更混乱化身:

object A1 extends Accessor {
    type A = A1.type
    def me = A1
}

class Item[+AA <: Accessor](acc: AA {type A = AA}) {
    acc.accept(this)
    A1.accept(this) // Compile error (good)
}

class Accessor {
    type A <: Accessor
    def me: A // can't do {type A = A} because its a cyclic error again
    def make = new Item[A](me) // Type Mismatch: found this.A, required this.A {type A = Accessor.this.A}
    def accept(item: Item[A]) = "accepted"
}

最后我尝试使 A 类型参数逆变,以便 Item[A] 是 Item[acc.type] 的子类型并且会被 acc 接受。

val a1 = new Accessor
val a2 = new Accessor
val item1 = a1.make
val item2 = a2.make
val itemA = new Item[Accessor](a2)
val item12 = new Item[A1.type](a2) // compile error (good)

a1.accept(itemA) // no compile error (bad), but I can prevent creation of Item[Accessor]s
a1.accept(item2) // compile error (good)

class Item[-A <: Accessor](acc: A) {
    acc.accept(this)
    val acc2 = new Accessor
    acc2.accept(this) // compile error (good) 
    // here Item[Accessor] <: Item[A] <: Item[acc.type] 
    // and Item[Accessor] <: Item[acc2.type]
    // but Item[A] is not necessarily <: Item[acc2.type]
}

class Accessor {
    def make() = new Item[this.type](this)
    def accept(item: Item[this.type]) = "accepted"
}

这最接近我尝试过的任何东西。唯一的问题是它填满了我的对象层次结构,因为我不能这样做:

class ImmutableAccessor extends Accessor

class ImmutableItem[-A <: ImmutableAccessor](acc: A) extends Item[A] // fails due to contravariance in A

如果有某种方法可以指定类型参数必须是单例类型。所以例如你可以说(我在这里发明了符号)

class Item[A:type <: Accessor](acc: A)

然后A 将是acc 的单例类型,我们会笑的。

【问题讨论】:

    标签: scala


    【解决方案1】:

    我们需要的秘诀是利用 Predef 中的 <: singleton>

    class Item[-A <: Accessor](acc: A)(implicit sing: A <:< Singleton) {
      acc.accept(this)
    }
    
    class Accessor {
      def make() = new Item[this.type](this)
      def accept(item: Item[this.type]) = "accepted"
    }
    
    class BetterItem[-B <: BetterAccessor](b: B)(implicit bsing: B <:< Singleton)
        extends Item[B](b)(bsing)
    }
    
    class BetterAccessor extends Accessor
    

    现在继承效果更好。我们可以做如下的事情

    val a1 = new Accessor
    val a2 = new Accessor
    val b3 = new BetterAccessor
    
    val i1 = a1.make
    val i3 = b3.make
    
    a1.accept(i1)     // GOOD
    //a2.accept(i1)   // compile time error
    //b3.accept(i1)   // compile time error
    //new Item[Accessor](a2)   // compile time error
    //a1.accept(i3)   // compile time error
    b3.accept(i3)     // GOOD
    

    我们会在我们想要的地方得到编译时错误。

    【讨论】:

      【解决方案2】:

      我没有关于处理继承的完整解决方案,但我可以建议进行一些小的更改以防止出现类似

      a1.accept(itemA)
      

      来自编译。在 Item 的定义中包含一个显式的“with Singleton”。

      class Item[-A <: Accessor with Singleton](acc: A) {
      

      然后尝试创建新的“Item[Accessor]”将无法编译。

      【讨论】:

      • 那个“with Singleton”的东西看起来像我想要的功能。它应该允许像 class Item[A &lt;: Accessor with Singleton](acc: A) 这样的语句来确保 A 与 acc.type 的类型完全相同,从而允许 Item 将自身传递给它的 Accessor。不幸的是,编译器似乎没有识别出这个逻辑,仍然认为 A >: acc.type.
      猜你喜欢
      • 1970-01-01
      • 2012-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-21
      • 1970-01-01
      相关资源
      最近更新 更多