簡體   English   中英

從抽象特征方法返回相同的類型

[英]Returning the same type from abstract trait method

讓我們說我們有一個特征,它有一些價值觀和一些操作。

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

這是使用抽象類型實現的。 我們在Self上有一個綁定類型,可以像這樣實現它:

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)

當我們想要具有特征類型的操作而不是具體類型時,問題開始:

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

自我類型的主要問題是這樣的事情:

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 quantication”之路:

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]]

最佳簽名是您建議的路徑依賴類型:

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

你甚至不需要type參數。 輸入f作為Foo會做(除非你在實際環境中需要A代替其他東西):

def setFiveValid3(f: Foo): f.Self = f.withX(5)

這不難看。 相反,它是路徑依賴類型的完美用途之一。 當你說它沒有明確表達意圖時,我也不同意:你明確表示結果將具有你所給予的論證的類型Self

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM