F-Bounded polymorphism with abstract types in Scala

℡╲_俬逩灬. 提交于 2019-12-06 03:15:40

问题


I have read several articles expressing that abstract types should be used to achieve f-bounded polymorphism in Scala. 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. 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:

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?


回答1:


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. 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.

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:

Favor 'object composition' over 'class inheritance'

-- (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 (e.g Bike, Car, Lorry). 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. 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




回答2:


See the discussion of F-bounded polymorphism in Twitter's Scala School.




回答3:


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)


来源:https://stackoverflow.com/questions/18557472/f-bounded-polymorphism-with-abstract-types-in-scala

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!