В качестве упражнения я попытался создать простой класс Fraction
, который бы поддерживал различные операции с цифрами c и так далее, но система типов Scala, правила для имплицитов и (отсутствие) документации делая меня действительно потерянным.
Моя текущая цель состоит в том, чтобы поддержать два следующих тестовых случая:
it should "support arithmetic" in {
val fraction1 = Fraction(3, 5)
val fraction2 = Fraction(7, 10)
val sum = fraction1 + fraction2
assertResult(Fraction(13, 10))(sum)
val diff = fraction1 - fraction2
assertResult(Fraction(-1, 10))(diff)
val prod = fraction1 * fraction2
assertResult(Fraction(21, 50))(prod)
val quot = fraction1 / fraction2
assertResult(Fraction(6, 7))(quot)
}
it should "support sequence operations" in {
val list = Fraction(1, 5) :: Fraction(0, 100) :: Fraction(2, 3) :: Nil
val sum = list.sum
assertResult(Fraction(13,15))(sum)
}
Тестовый случай для "это должно поддерживать арифметические операции c", которые я покрыл явными методами определено в моем классе. Затем я перешел к «должен поддерживать операции с последовательностями» и, следуя некоторым примерам, заработал, реализовав Numeric[Fraction]
класс типов следующим образом:
object Fraction {
def apply(numerator: Int, denominator: Int): Fraction = ...
def unapply(f: Fraction): Option[(Int, Int)] = ...
trait NumericFraction extends Numeric[Fraction] {
def fromInt(x: Int): Fraction = Fraction(x, 1)
def minus(x: Fraction, y: Fraction): Fraction = {
plus(x, negate(y))
}
def negate(x: Fraction): Fraction = Fraction(-x.numerator, x.denominator)
def plus(x: Fraction, y: Fraction): Fraction = { ... actual addition here ... }
def times(x: Fraction, y: Fraction): Fraction = { ... multiplication here ... }
def toDouble(x: Fraction): Double = x.numerator.toDouble / x.denominator
... other Numeric[T] implementations...
}
implicit object NumericFraction extends NumericFraction
}
Затем я мог бы изменить операторы в своем классе, чтобы повторно использовать реализации from NumericFraction
:
class Fraction private (val numerator: Int, val denominator: Int)(implicit num: Fraction.NumericFraction) {
import num._
... some stuff ...
def +(other: Fraction): Fraction = plus (this, other)
def -(other: Fraction): Fraction = minus(this, other)
def *(other: Fraction): Fraction = times(this, other)
... more stuff ...
}
Кажется, что здесь все работает, но я, кажется, упускаю некоторый момент: поскольку я реализовал Numeric[Fraction]
, который, в свою очередь, обеспечивает Ops
, который, в свою очередь, определяет арифметику c операторы +
, -
и *
, могу ли я использовать их как-то как арифметические c операторы для моего класса, не определяя явно методы операторов в моем классе? Если да, то как? Если нет, то для чего тогда Numeric[T].Ops
? Будучи нубом, который заблудился в адском имплиците и полон ожиданий, вытекающих из неверно понятых идей, я ожидал, что можно будет опустить метод *
в моем классе и использовать вместо него Numeric[Fraction].Ops.*
в клиентском коде, в выражениях типа val f3 = Fraction(n1, d1) * Fraction(n2, d2)
- что я не так делаю?