gpt4 book ai didi

scala - 对于返回电流类型问题,F 有界多态性相对于类型类的优势

转载 作者:行者123 更新时间:2023-12-04 21:01:24 25 4
gpt4 key购买 nike

Returning the current type StackOverflow 上经常被问到问题。 Here就是这样一个例子。通常的答案似乎是 F-bounded polymorphismtypeclass模式解决方案。奥德斯基建议在 Is F-bound polymorphism useful?

F-bounds do indeed add significant complexity. I would love to be able to get rid of them, and replace them with higher-kinded subtyping



而 tpolecat(链接 post 的作者)建议

A better strategy is to use a typeclass, which solves the problem neatly and leaves little room for worry. In fact it’s worth considering abandoning subtype polymorphism altogether in these situations.



以下 disadvantage被识别

F-bounded polymorphism parameterizes a type over its own subtypes, which is a weaker constraint than what the user usually wants, which is a way to say “my type”, which you can’t express precisely via subtyping. However typeclasses can express this idea directly, so that’s what I would teach beginners



我的问题是,根据上述建议,有人可以证明 F 有界多态性是有利的情况,还是我们应该将类型类解决方案作为解决返回电流类型问题的规范答案?

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-bounded polymorphism by type member
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)

最佳答案

F-有界是类型系统能够表达的一个很好的例子,甚至是更简单的类型系统,比如 Java。但是,一个 类型类 永远是更安全、更好的选择。

我们所说的更安全是什么意思?简单地说,我们不能违反返回完全相同类型的契约(Contract)。可以对的两种形式进行F-有界多态性(很容易)。

类型成员的 F 有界多态性

这个很容易破解,因为我们只需要对类型成员撒谎。

trait Pet {
type P <: Pet
def name: String
def renamed(newName: String): P
}

final case class Dog(name: String) extends Pet {
override type P = Dog
override def renamed(newName: String): Dog = Dog(newName)
}

final case class Cat(name: String) extends Pet {
override type P = Dog // Here we break it.
override def renamed(newName: String): Dog = Dog(newName)
}

Cat("Luis").renamed(newName = "Mario")
// res: Dog = Dog("Mario")

类型参数的 F 有界多态性

这个比较难破解,因为 this: A强制扩展类是相同的。但是,我们只需要添加一个额外的继承层。
trait Pet[P <: Pet[P]] { this: P =>
def name: String
def renamed(newName: String): P
}

class Dog(override val name: String) extends Pet[Dog] {
override def renamed(newName: String): Dog = new Dog(newName)

override def toString: String = s"Dog(${name})"
}

class Cat(name: String) extends Dog(name) // Here we break it.

new Cat("Luis").renamed(newName = "Mario")
// res: Dog = Dog(Mario)

尽管如此,很明显 类型类 方法更复杂,有更多样板;此外,有人可以争辩说要打破 F-有界 ,你必须故意这样做。因此,如果您对 的问题感到满意, F-有界并且不喜欢处理 的复杂性类型类 那么它仍然是一个有效的解决方案。

另外,我们应该注意,即使是 类型类 可以通过使用 asInstanceOf 之类的东西来打破方法或反射(reflection)。

顺便说一句,值得一提的是,如果您不想返回修改后的副本,而是想要修改当前对象并返回自身以允许链接调用(如传统的 Java 构建器),您可以(应该)使用 this.type .
trait Pet {
def name: String

def renamed(newName: String): this.type
}

final class Dog(private var _name: String) extends Pet {
override def name: String = _name

override def renamed(newName: String): this.type = {
this._name = newName
this
}

override def toString: String = s"Dog(${name})"
}

val d1 = Dog("Luis")
// d1: Dog = Dog(Luis)

val d2 = d1.renamed(newName = "Mario")
// d2: Dog = Dog(Mario)

d1 eq d2
// true

d1
// d1: Dog = Dog(Mario)

关于scala - 对于返回电流类型问题,F 有界多态性相对于类型类的优势,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59813323/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com