2013-01-22 59 views
1

我试图用抽象类型,以简化和澄清类型处理,但我不断遇到看似无意义的错误,像这样的:摘要和参数化类型在斯卡拉

trait Delta[A] { 
    def apply(c: A) 
} 

abstract class ValueHolder { 

    type Value 
    type Event <: Delta[ValueHolder] 

    def update(next: Value): Event = new UpdateEvent(next) 
    // type mismatch; found : UpdateEvent[ValueHolder] 
    // required: ValueHolder.this.Event 
} 

class UpdateEvent[C <: ValueHolder](next: C#Value) extends Delta[C] { 

    def apply(c: C) = c.update(next) 
    // type mismatch; found : 
    // UpdateEvent.this.next.type (with underlying type C#Value) 
    // required: c.Value 
} 
  1. Delta[C] ,其中C <: ValueHolder,从而符合Event <: Delta[ValueHolder]

  2. 同样,鉴于cC,不是c.Value a C#Value

我可以使用一个强制转换来删除第二个错误,但是这会破坏使用类型的要点。

我试图纳入答案[此相关的问题]建议[1] ...

class UpdateEvent[C <: ValueHolder, V <: C#Value](next: V) extends Delta[C] { 

...其中,不幸的是,未能缓解任何一个问题(尽管它需要一些更有型参数从update()中调用时)。

帮助???


更新:不幸的是,我上面给的例子是过于简单。我试图传播对具有相同方法签名(尽管可能不同类型参数)的类的更改,因此它们充当原始视图的“视图”。

例如,假设你可以运行这个命令:

(ListBuffer[Int]:_).map(_.toString) 

...然后有导致ListBuffer[String]更新每次原来是这样。 (因为我不能简单地解释一些原因,所以没有反复运行“map”)。就像这个小例子一样,其他人定义了正在实现的特征,这意味着我不能改变方法特征来解决这个问题。 (注:我也不能摆脱type Event,因为有一个变量(这里没有说明)包含所有收到每个Event的听众 - 而且这种类型应该由子类改进以允许更具体的类型)

无论如何,经过很多时间思考不太明显的Scala参考手册(信息都在那里,但它假设你已经知道很多)后,我终于想出如何约束UpdateEvent使C和C#值对应:

class UpdateEvent[V, C <: ValueHolder { type Value = V }](
    next: V) extends Delta[C] { ... } 

这可以修复这两个编译错误并保留现有方法。但我认为彼得的答案(下文)是正确的(给予他的声望点),因为我非常欣赏他的花费时间。谢谢彼得,对你最好。

回答

2

Delta[C],其中C <: ValueHolder,从而符合Event <: Delta[ValueHolder]

我对此并不完全确定,所以以下为理论。如果Delta的类型参数是协变的(即,Delta[+A]),编译器只能确保Delta[C],其中C <: ValueHolder是-a Delta[ValueHolder]。但目前它是不变的,所以以上是不正确的。编译器也不知道Delta[C]是否为EventEvent是抽象类型,即的一些类型的占位符,稍后将被定义,这将是Delta[ValueHolder]的子类型。但是,它可以被定义为的任何这样的子类,而不仅仅是UpdateEvent!所以我可以定义一个ValueHolder子类,它的Event类型为OtherEvent,因此它不是UpdateEvent。如果编译器允许这样做,最终结果将是运行时错误。

同样,因为cC,不c.Value一个C#Value

事实确实如此。但看看错误消息:

// type mismatch; found : 
    // UpdateEvent.this.next.type (with underlying type C#Value) 
    // required: c.Value 

也就是说,编译器需要一个类型的c.Value,它有一个C#Value代替。而C#Value不是 a c.Value - 前者是更一般的类型!的潜在的解决方案

部分可以是使update参数具有结合的类型参数C <: ValueHolder,然后使用C#Value作为参数的类型,而不是ValueHolder#Value。这将消除第二个错误。第一个问题的解决方案可能是用Delta[C]替换返回类型Event。因此,下面的编译:

trait Delta[A] { 
    def apply(c: A): Delta[A] 
} 

abstract class ValueHolder { 

    type Value 

    def update[C <: ValueHolder](next: C#Value): Delta[C] = new UpdateEvent(next) 
} 

class UpdateEvent[C <: ValueHolder](next: C#Value) extends Delta[C] { 

    override def apply(c: C) = c.update(next) 
} 

注:

    在该方案中
  • Event实际上不再被使用,所以它可以被完全去除
  • 我所定义的Delta.apply返回类型为Delta[A],按照实际返回的类型UpdateEvent.apply
  • 由于这个原因,类型参数A在这里只能是不变的,因为A现在站在两者都在Delta中的协变(作为方法返回类型)和逆变(作为方法参数)位置。

希望这有助于 - 我不知道你想实现但什么,所以我可能已经宰你最初的想法与这些修改:-)

+0

真!但是,如何指定与特定价值风格相关的特定价值?我已经尝试了很多变体(C.Value,C.Value.type,在各个地方传递参数)...无济于事。 – Tim

+0

@Tim,请检查我的更新。 –

+0

彼得!非常感谢*刺痛了这个! (1)在update()上删除返回签名是正确的 - 好点! (2)不幸的是,这只是为了这个问题而作出的改变的副作用;在现实生活中,我无法更改(这里是什么)“update()”签名,因为我试图扩展Map和其他着名的Scala签名。可悲的是,我可能不得不更新这个问题来解释更多的限制。不过谢谢你。 – Tim