在 StackOverflow 上经常会问到返回当前类型的问题。这是一个这样的例子。通常的答案似乎是F 有界多态性或类型类模式解决方案。Odersky 在Is F-bound polymorphism 有用吗?
F-bounds 确实增加了显着的复杂性。我希望能够摆脱它们,并用更高级的子类型替换它们
而 tpolecat (链接帖子的作者)建议
一个更好的策略是使用类型类,它可以巧妙地解决问题并且几乎没有担心的余地。事实上,在这些情况下完全放弃亚型多态性是值得考虑的。
确定了以下缺点
F-bounded polymorphism 将一个类型参数化为它自己的子类型,这是一个比用户通常想要的更弱的约束,这是一种说“我的类型”的方式,你不能通过子类型精确地表达它。然而类型类可以直接表达这个想法,所以这就是我要教初学者的
我的问题是,根据上述建议,有人可以证明 F 有界多态性是有利的情况,还是我们应该将类型类解决方案作为解决返回电流类型问题的规范答案?
类型参数的 F 绑定多态性
trait Semigroup[A <: Semigroup[A]] { this: A =>
def combine(that: A): A
}
final case class Foo(v: Int) extends Semigroup[Foo] {
override def combine(that: Foo): Foo = Foo(this.v + that.v)
}
final case class Bar(v: String) extends Semigroup[Bar] {
override def combine(that: Bar): Bar = Bar(this.v concat that.v)
}
def reduce[A <: Semigroup[A]](as: List[A]): A = as.reduce(_ combine _)
reduce(List(Foo(1), Foo(41))) // res0: Foo = Foo(42)
reduce(List(Bar("Sca"), Bar("la"))) // res1: Bar = Bar(Scala)
类型成员的 F 有界多态性
trait Semigroup {
type A <: Semigroup
def combine(that: A): A
}
final case class Foo(v: Int) extends Semigroup {
override type A = Foo
override def combine(that: Foo): Foo = Foo(this.v + that.v)
}
final case class Bar(v: String) extends Semigroup {
override type A = Bar
override def combine(that: Bar): Bar = Bar(this.v concat that.v)
}
def reduce[B <: Semigroup { type A = B }](as: List[B]) =
as.reduce(_ combine _)
reduce(List(Foo(1), Foo(41))) // res0: Foo = Foo(42)
reduce(List(Bar("Sca"), Bar("la"))) // res1: Bar = Bar(Scala)
类型类
trait Semigroup[A] {
def combine(x: A, y: A): A
}
final case class Foo(v: Int)
object Foo {
implicit final val FooSemigroup: Semigroup[Foo] =
new Semigroup[Foo] {
override def combine(x: Foo, y: Foo): Foo = Foo(x.v + y.v)
}
}
final case class Bar(v: String)
object Bar {
implicit final val BarSemigroup: Semigroup[Bar] =
new Semigroup[Bar] {
override def combine(x: Bar, y: Bar): Bar = Bar(x.v concat y.v)
}
}
def reduce[A](as: List[A])(implicit ev: Semigroup[A]): A = as.reduce(ev.combine)
reduce(List(Foo(1), Foo(41))) // res0: Foo = Foo(42)
reduce(List(Bar("Sca"), Bar("la"))) // res1: Bar = Bar(Scala)