Рассмотрим подход типа типов
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
, который выводит
import GameStatistics._
res0: SimpleGameStatistics = SimpleGameStatistics(6.9,50)
res1: SimpleGameStatistics = SimpleGameStatistics(6.9,50)
res2: SimpleGameStatistics = SimpleGameStatistics(294.0,7)
Обратите внимание, что этот вид бинарных операций "сложения" является очень распространенным шаблоном, для которого коты предоставляют абстракцию под названием 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)
, мы сможем подключиться ко всем вкусностям cats
предоставляет готовые решения, такие как |+|
инфиксный оператор
import cats.implicits._
SimpleGameStatistics(42, 7) |+| SimpleGameStatistics(8, 43)
List(SimpleGameStatistics(42, 7), SimpleGameStatistics(8, 43)).reduce(_ |+| _)