如何在Scala中将方法参数限制为子类类型

mis*_*111 1 generics scala

我有一个特征GameStatistics,它定义了一个add()方法,该方法接受一个参数并返回其自身和参数的总和。子类中的实现应该只接受它们自己类型的实例作为参数(或者也可能是子类型)。

我想使用此add方法来聚合列表GameStatistics,使用 Seq 的reduce方法。

我无法在 Scala 中定义它并使其编译。下面是我尝试过的一个示例及其编译错误。

这些错误对我来说没有任何意义。我应该如何让它发挥作用?

package bgengine

trait GameStatistics {
  def equity: Double

  def add[G: this.type](s: G): G

  def multiply(x: Double): GameStatistics
}

object GameStatistics {
  def aggregate(stats: Seq[GameStatistics]): GameStatistics = stats.reduce( _ add _ )
}

case class SimpleGameStatistics(equity: Double, nrGames: Int) extends GameStatistics {

  override def add[G: SimpleGameStatistics](s: G): G =
    SimpleGameStatistics((equity * nrGames + s.equity * s.nrGames) / (nrGames + s.nrGames), nrGames + s.nrGames).asInstanceOf[G]

  override def multiply(x: Double): SimpleGameStatistics = SimpleGameStatistics(equity * x, nrGames)
}
Run Code Online (Sandbox Code Playgroud)

错误:(6, 12) GameStatistics.this.type 不采用类型参数
def add[G: this.type](s: G): G

错误:(17, 21) bgengine.SimpleGameStatistics 不采用类型参数 override def add[G: SimpleGameStatistics](s: G): G =

错误:(18, 48) 价值权益不是类型参数 G SimpleGameStatistics((equity * nrGames + s.equity * s.nrGames) / (nrGames + s.nrGames), nrGames + s.nrGames).asInstanceOf[ 的成员G]

错误:(18, 59) 值 nrGames 不是类型参数 G SimpleGameStatistics((equity * nrGames + s.equity * s.nrGames) / (nrGames + s.nrGames), nrGames + s.nrGames).asInstanceOf[ 的成员G]

错误:(18, 83) 值 nrGames 不是类型参数 G SimpleGameStatistics((equity * nrGames + s.equity * s.nrGames) / (nrGames + s.nrGames), nrGames + s.nrGames).asInstanceOf[ 的成员G]

错误:(18, 105) 值 nrGames 不是类型参数 G SimpleGameStatistics((equity * nrGames + s.equity * s.nrGames) / (nrGames + s.nrGames), nrGames + s.nrGames).asInstanceOf[ 的成员G]

Mar*_*lic 5

考虑类型类方法

case class SimpleGameStatistics(equity: Double, nrGames: Int)

trait GameStatistics[G] {
  def add(a: G, b: G): G
  def multiply(x: Double, a: G): G
}

object GameStatistics {
  implicit val simpleGameStatistics = new GameStatistics[SimpleGameStatistics] {
    def add(a: SimpleGameStatistics, b: SimpleGameStatistics) = SimpleGameStatistics((a.equity * a.nrGames + b.equity + b.nrGames) / (a.nrGames + b.nrGames), a.nrGames + b.nrGames)
    def multiply(x: Double, a: SimpleGameStatistics) = SimpleGameStatistics(a.equity * x, a.nrGames)
  }

  implicit class StatsOps[G](private val a: G) {
    def add(b: G)(implicit ev: GameStatistics[G]): G = ev.add(a, b)
    def multiply(x: Double)(implicit ev: GameStatistics[G]): G = ev.multiply(x, a)
  }
  implicit class AggregateOps[G](private val stats: List[G]) {
    def aggregateStats(implicit ev: GameStatistics[G]): G = stats.reduce(_ add _)
  }
}


import GameStatistics._
SimpleGameStatistics(42, 7) add SimpleGameStatistics(8, 43)
List(SimpleGameStatistics(42, 7), SimpleGameStatistics(8, 43)).aggregateStats
SimpleGameStatistics(42, 7) multiply 7
Run Code Online (Sandbox Code Playgroud)

哪个输出

import GameStatistics._
res0: SimpleGameStatistics = SimpleGameStatistics(6.9,50)
res1: SimpleGameStatistics = SimpleGameStatistics(6.9,50)
res2: SimpleGameStatistics = SimpleGameStatistics(294.0,7)
Run Code Online (Sandbox Code Playgroud)

请注意,这些“加法”二元运算是一种非常常见的模式,cats 为其提供了一个名为 的抽象Semigroup,因此如果我们提供Semigroup实例SimpleGameStatistics

import cats.Semigroup

implicit val intAdditionSemigroup: Semigroup[SimpleGameStatistics] =
  (a: SimpleGameStatistics, b: SimpleGameStatistics) => SimpleGameStatistics((a.equity * a.nrGames + b.equity + b.nrGames) / (a.nrGames + b.nrGames), a.nrGames + b.nrGames)
Run Code Online (Sandbox Code Playgroud)

我们可以使用所有cats开箱即用的好东西,例如|+|中缀运算符

import cats.implicits._

SimpleGameStatistics(42, 7) |+| SimpleGameStatistics(8, 43)
List(SimpleGameStatistics(42, 7), SimpleGameStatistics(8, 43)).reduce(_ |+| _)
Run Code Online (Sandbox Code Playgroud)