Scala中的抽象和参数化类型

时间:2013-01-22 13:04:02

标签: scala types

我正在尝试使用抽象类型来简化和澄清类型处理,但我一直遇到看似非感性的错误:

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.ValueC#Value

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

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

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

    ......遗憾的是,它无法解决这两个问题(虽然从update()调用时需要更多类型参数)。

    帮助???


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

    例如,假设你可以运行它:

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

    ...然后每次原始时更新结果ListBuffer[String]。 (不只是运行“地图”,一遍又一遍,原因我不能简单介绍一下。)至于这个小小的例子,其他的定义所实现的特点,这意味着我不能改变的方法签名来解决此问题。< / p>

    (注意:我也无法摆脱type Event,因为有一个变量(此处未说明)包含收到每个Event的所有听众 - 其类型应该被细化通过子类为每个子类提供更具体的侦听器。)

    反正很多时间琢磨的不是非常自明斯卡拉参考手册后(信息都在那里,但它假定你知道很多的话),我终于想通了如何限制UpdateEvent,因此C和C#值对应:

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

    这可以修复编译错误并保留现有方法。但我正在将彼得的回答(下图)标记为正确(给予他声望点),因为我非常感谢他花时间在上面。谢谢,彼得,对你最好。

1 个答案:

答案 0 :(得分:2)

  

Delta[C]C <: ValueHolder哪里符合Event <: Delta[ValueHolder]

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

  

同样,鉴于cC,不是c.ValueC#Value

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

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

也就是说,编译器需要一种c.Value类型,而它需要C#Value。而C#Value 一个c.Value - 前者是更一般的类型!

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

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内。

希望这会有所帮助 - 我不确定你想要达到的目标,所以我可能会通过这些修改扼杀你原来的想法: - )