简体   繁体   English

F-Bounded多态与Scala中的抽象类型

[英]F-Bounded polymorphism with abstract types in Scala

I have read several articles expressing that abstract types should be used to achieve f-bounded polymorphism in Scala. 我已经阅读了几篇文章,表示应该使用抽象类型来实现Scala中的f-bounded多态。 This is primarily to alleviate type inference issues, but also to remove the quadratic growth that type parameters seem to introduce when defining recursive types. 这主要是为了缓解类型推断问题,同时也消除了类型参数在定义递归类型时似乎引入的二次增长。

These are defined as so: 这些定义如下:

trait EventSourced[E] {
  self =>

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

  def apply(event: E): FBound
}

However, this appears to introduce two issues: 但是,这似乎引入了两个问题:

1) Each time a user wants to reference an object of this type, they must also refer to the FBound type parameter. 1)每次用户想要引用此类型的对象时,它们还必须引用FBound类型参数。 This feels like a code smell: 这感觉就像代码味道:

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

2) The compiler is now unable to infer type parameters for methods such as the above, failing with the message: 2)编译器现在无法推断上述方法的类型参数,但没有消息:

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

Is anyone out there using a successful implementation of f-bounded polymorphism in their solution, whereby the compiler is still able to infer types? 是否有人在其解决方案中使用f-bounded多态的成功实现,编译器仍然可以推断类型?

I've since realised that f-bounded polymorphism should be avoided in most cases - or rather - there's usually an alternative design that you should opt for. 我已经意识到在大多数情况下应该避免使用f-bounded多态 - 或者更确切地说 - 通常还有一种你应该选择的替代设计。 To understand how to avoid it, we first need to know what makes us require it: 要了解如何避免它,我们首先需要知道是什么让我们需要它:

F-bounded polymorphism occurs when a type expects important interface changes to be introduced in derived types. 当类型期望在派生类型中引入重要的接口更改时 ,会发生F-bounded多态。

This is avoided by composing the expected areas of change instead of attempting to support them via inheritance. 通过编写预期的更改区域而不是尝试通过继承来支持它们来避免这种情况。 This actually comes back to Gang of Four design patterns: 这实际上回到了Gang of Four设计模式:

Favor 'object composition' over 'class inheritance' 赞成'对象组合'而不是'类继承'

-- (Gang of Four, 1995) - (Gang of Four,1995)

For example: 例如:

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

becomes: 变为:

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

Here, the 'expected change' is the vehicle type (eg Bike , Car , Lorry ). 这里,“预期变化”是车辆类型(例如, BikeCarLorry )。 The previous example assumed this would be added through inheritance, requiring an f-bounded type that made inference of W impossible for any function using Vehicle. 前面的例子假设这将通过继承添加,需要一个f-bounded类型,使得使用Vehicle的任何函数都不可能推断W The new method, which uses composition, does not exhibit this problem. 使用合成的新方法不会出现此问题。

See: https://github.com/ljwagerfield/scala-type-inference/blob/master/README.md#avoiding-f-bounded-polymorphism 请参阅: https//github.com/ljwagerfield/scala-type-in​​ference/blob/master/README.md#avoiding-f-bounded-polymorphism

请参阅Twitter Scala学校F-bounded多态的讨论。

I am probably missing something with the following implementation. 我可能错过了以下实现的东西。

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM