【问题标题】:F-Bounded polymorphism with abstract types in ScalaScala 中具有抽象类型的 F 有界多态性
【发布时间】:2013-09-01 10:25:05
【问题描述】:

我读过几篇文章,表达了抽象类型应该用于在 Scala 中实现 f 有界多态性。这主要是为了缓解类型推断问题,但也是为了消除在定义递归类型时类型参数似乎引入的二次增长。

这些是这样定义的:

trait EventSourced[E] {
  self =>

  type FBound <: EventSourced[E] { type FBound <: self.FBound }

  def apply(event: E): FBound
}

然而,这似乎引入了两个问题:

1) 每次用户想要引用此类型的对象时,他们还必须引用FBound 类型参数。这感觉像是代码异味:

def mapToSomething[ES <: EventSourced[E], E](eventSourced: ES#FBound): Something[ES, E] = ...

2) 编译器现在无法推断上述方法的类型参数,失败并显示以下消息:

Type mismatch, expected: NotInferredES#FBound, actual: MyImpl#FBound

是否有人在他们的解决方案中成功实现了 f 有界多态性,从而使编译器仍然能够推断类型?

【问题讨论】:

  • Scala 集合库成功地使用了 F 有界多态性,没有任何问题。它使用类型参数而不是类型成员,您可能想尝试基于此的解决方案。
  • 请你给我一个例子来看看,即图书馆的哪些部分这样做?
  • 参见标准库中的List。请注意在继承 GenericTraversableTemplate 和 LinearSeqOptimized 时使用的 F 有界多态性。
  • 谢谢,我将在这里调查 Martin 对 f 有界多态性的使用,看看编译器是否存在与我的代码使用类型参数时相同的类型推断问题:D
  • 您能告诉我们您希望这段代码具体做什么吗? mapToSomething 应该返回什么?

标签: scala generics polymorphism scala-2.10


【解决方案1】:

我已经意识到在大多数情况下应该避免 f 有界多态性 - 或者更确切地说 - 通常你应该选择另一种设计。要了解如何避免它,我们首先需要知道是什么让我们需要它:

当一个类型期望在派生类型中引入重要的接口更改时,就会发生F-bounded polymorphism。

这可以通过组合预期的变化区域来避免,而不是尝试通过继承来支持它们。这实际上又回到了四人组设计模式:

倾向于“对象组合”而不是“类继承”

--(四人帮,1995)

例如:

trait Vehicle[V <: Vehicle[V, W], W] {
    def replaceWheels(wheels: W): V
}

变成:

trait Vehicle[T, W] {
    val vehicleType: T
    def replaceWheels(wheels: W): Vehicle[T, W]
}

这里,“预期变化”是车辆类型(例如BikeCarLorry)。前面的示例假设这将通过继承添加,需要一个 f 有界类型,这使得使用 Vehicle 的任何函数都无法推断 W。使用组合的新方法不会出现这个问题。

见:https://github.com/ljwagerfield/scala-type-inference/blob/master/README.md#avoiding-f-bounded-polymorphism

【讨论】:

  • > 使用合成的新方法不会出现这个问题 不太正确。如果你想保留类型级别的检查,你最终会需要它,比如VehicleType[V &lt;: VehicleType]
【解决方案2】:

【讨论】:

  • 此示例使用类型参数,因此会导致推理问题。在我的示例中,无法在调用站点推断以下方法定义的类型: def doSomething[ES <: eventsourced e es>
  • 在来自 Twitter 或 Scala 库的示例中,F-bound 类型参数在类本身上,而不是在方法上。 F-bounded polymorphism 让方法引用this 的类型,它最终在子类中的确切类型。以TraversableLikemap的定义为例,继承于Listdef map[B, That](f: A =&gt; B)(implicit bf: CanBuildFrom[Repr, B, That]): That。 repr是F-Bound类型参数,是List里面的List[A],但是TraversableLike里面的代码不用List知道就可以写。
  • 是的,我认为最终我需要重新考虑我的一些设计。在我看来,当从 f 有界超类型继承时,应该只在类型定义级别使用 f 有界多态性。但是,在我的代码中,有几个在方法级别使用它的实例......我觉得这需要改变。
  • 我点击了链接,但只有类型参数实现,没有关于 f 绑定多态的抽象类型实现的单一提示
  • @ayvango 抽象内部类型和类型参数在 Scala 中是等价的。鉴于类型参数更简洁,没有理由使用抽象类型编写示例。
【解决方案3】:

我可能在以下实现中遗漏了一些东西。

trait EventSourced[E] {
  self =>

  type FBound <: EventSourced[E] { type FBound <: self.FBound }

  def apply(event: E): FBound
}

trait Something[ES, E]

def mapToSomething[E](
  eventSourced: ES forSome {
    type ES <: EventSourced[E]
  }): Something[eventSourced.type, E] = ???

class Test extends EventSourced[Boolean] {
  type FBound = Test
  def apply(event:Boolean):FBound = ???
}

val x:Test  = ???

mapToSomething(x)

【讨论】:

    猜你喜欢
    • 2016-09-22
    • 1970-01-01
    • 2015-03-09
    • 1970-01-01
    • 2018-05-20
    • 1970-01-01
    • 2015-09-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多