scala - 从抽象特征方法返回相同的类型

标签 scala typeclass traits abstract-type

假设我们有一个特征,它有一些值和一些操作。

trait Foo {
  type Self <: Foo
  val x: Int
  def withX(x: Int): Self
}

这是使用抽象类型实现的。我们在 Self 上绑定(bind)了一个类型,可以像这样实现它:
case class Foo1(x: Int) extends Foo {
  type Self = Foo1
  def withX(x: Int) = copy(x = x)
}

没事儿。我们可以使用该方法,并且我们看到该类型是静态保留的。
scala> Foo1(10).withX(5)
res0: Foo1 = Foo1(5)

当我们想要使用 trait 类型而不是具体类型进行操作时,问题就开始了:
object Foo {
//Error:(13, 43) type mismatch;
//found   : f.Self
//required: A
//  def setFive[A <: Foo](f: A): A = f.withX(5)
}

好吧,我们不能完全这样做,因为编译器不知道 Foo#Self 将被分配给什么类型。但我们知道它是同一类型。

当然,使用丑陋的方法效果很好:
object Foo {
  // Ugly type signature
  def setFiveValid[A <: Foo](f: A): A#Self = f.withX(5)

  // Another ugly type signature
  def setFiveValid2[A <: Foo](f: A): f.Self = f.withX(5)
}

他们都没有非常清楚地表达意图。

不过,我们可以使用类型类来解决它。
case class Foo2(x: Int)

trait FooOps[A] extends Any {
  def a: A
  def withX(x: Int): A
}

object Foo2 {
  implicit class Foo2Ops(val a: Foo2) extends AnyVal with FooOps[Foo2] {
    def withX(x: Int) = a.copy(x = x)
  }
}

object Foo {
  // View bounds approach.
  def setFiveValid3[A <% FooOps[A]](f: A): A = f.withX(5)
}

但是,这仍然非常嘈杂。

有没有更好的方法来实现setFive ?

编辑 1

self 类型的主要问题是这样的:
Error:(24, 11) type mismatch;
 found   : app.models.world.WObject.WorldObjUpdate[self.Self] => app.models.world.WObject.WorldObjUpdate[self.Self]
    (which expands to)  app.models.game.events.Evented[(app.models.world.World, self.Self)] => app.models.game.events.Evented[(app.models.world.World, self.Self)]
 required: app.models.world.WObject.WorldObjUpdate[self.Self] => app.models.game.events.Evented[(app.models.world.World, Self)]
    (which expands to)  app.models.game.events.Evented[(app.models.world.World, self.Self)] => app.models.game.events.Evented[(app.models.world.World, Self)]
          identity
          ^

然后再次使用看起来很奇怪的签名和样板:
  def attackReachable(
    data: WObject.WorldObjUpdate[Self]
  ): WObject.WorldObjUpdate[data.value._2.Self]

最佳答案

你可以走“F-bounded quantification”这条路:

trait Foo[F <: Foo[F]] {
  def withX(x: Int): F
}

object Foo {
  def setFive[F <: Foo[F]](f: F): F = f.withX(5)
}

我成功地使用了很多,但它的代价是不得不写 F <: Foo[F]]无处不在。

关于scala - 从抽象特征方法返回相同的类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26105733/

相关文章:

algorithm - 使用 Scala 的动态编程来解决来自 CodeChef 的 Mixture

java - 如何强制 Scala 中指令的执行顺序

Scala 编程一堆 if 的方法

scala - 类型类中的多个类型参数

java - 如何在 Java/Scala 中动态、递归地修饰一个类?

scala - Shapeless:这两种实例推导方法有什么区别?

haskell - Haskell 中类型类实例的类型约束?

generics - 使用泛型函数制作盒装特征会返回错误

javascript - javascript 中的特性

Scala可堆叠特征