如何使用scala的单例对象类型来要求对象是由其构造函数参数之一创建的?

时间:2012-10-17 17:06:03

标签: scala

我有一个Accessor类来检索Items。它还可以将Item作为参数,并从数据库返回该项的最新版本。当它创建一个Item时,它将自身作为参数传递给Item。

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

这样做的难点在于Item类定义中的类型参数,如此

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

不能指代acc本身的类型。从项目acc.type <: A <: 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的单身类型,我们会笑。

2 个答案:

答案 0 :(得分:1)

我们需要的秘诀就是利用&lt;:&lt;在Predef中找到的类,使用隐式值。对于Singleton&#34;你是对的。没有做我们想做的一切。

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

我们将编译时错误准确地发送到我们想要的地方。

答案 1 :(得分:0)

我没有关于处理继承的完整解决方案,但我可以建议一个小的改动以防止像

这样的行
a1.accept(itemA)

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

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

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