返回当前类型的问题在StackOverflow上经常被问到。这里是一个例子。通常的答案似乎要么是F-bounded polymorphism,要么是typeclass模式解决方案。Odersky在F-bound polymorphism useful吗?中建议:
F-bounds确实增加了相当大的复杂性。我很想能够摆脱它们,并用高级子类型替换它们。
而tpolecat(链接post的作者)则建议:
更好的策略是使用类型类,它可以干净利落地解决问题,几乎没有什么可担心的余地。事实上,在这些情况下考虑完全放弃子类型多态性是值得的。
其中确定了以下disadvantage:
F-bounded多态性是将类型参数化为其自身的子类型,这是用户通常想要的比较弱的约束条件,用户通常希望以一种方式表达“我的类型”,这不能通过子类型精确地表达。然而,类型类可以直接表达这个想法,所以这就是我会教初学者的东西。我的问题是,在上述建议的基础上,是否有人能演示出F-bounded多态性有利的情况,或者我们应该将类型类解决方案作为解决“返回当前类型”问题的规范答案?
F-bound polymorphism by type parameter.
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-bounded多态
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)
type A <: Monoid
会有什么问题? - Mario Galictrait Monoid { type A <: Monoid }
与trait Monoid[A <: Monoid[_]]
类似。 - Dmytro MitinMonoid
就是一种存在类型。 - Dmytro Mitin