【发布时间】:2020-05-05 20:59:15
【问题描述】:
Returning the current type 问题经常在 StackOverflow 上被问到。 Here 就是这样一个例子。通常的答案似乎是F-bounded polymorphism 或typeclass 模式解决方案。奥德斯基建议Is F-bound polymorphism useful?
F-bounds 确实增加了很大的复杂性。我很想能够 摆脱它们,并用更高级的子类型替换它们
而tpolecat(链接post的作者)建议
更好的策略是使用类型类,它可以解决问题 整洁,几乎没有担心的余地。其实很值得 考虑在这些中完全放弃亚型多态性 情况。
以下disadvantage 的标识
F-bounded polymorphism 将一个类型参数化为它自己的子类型, 这是一个比用户通常想要的更弱的约束, 是一种表达“我的类型”的方式,您无法通过以下方式精确表达 子类型。然而类型类可以直接表达这个想法,所以 这就是我要教初学者的内容
我的问题是,根据上述建议,有人可以证明 F 有界多态性是有利的情况,还是我们应该将 typeclass 解决方案作为解决 return-current-type 有问题吗?
类型参数的 F-bound 多态性
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)
【问题讨论】:
-
@LuisMiguelMejíaSuárez this 你所说的F-bounded by type member而不是类型参数是什么意思?
-
是的,完全一样。然而,Monoid 是一个非常好的例子,说明了何时完全需要 typeclass。
-
@LuisMiguelMejíaSuárez 啊,我明白了,因为
unit。我将其更改为半组。 -
@MarioGalic 实际上对于真正的 F-bound 这应该是
trait Monoid { self => type A <: Monoid { type A = self.A }...而不仅仅是trait Monoid { type A <: Monoid... -
@DmytroMitin 与
type A <: Monoid有什么关系?
标签: scala typeclass f-bounded-polymorphism return-current-type