Как правильно использовать Scala Numeri c класс типов и повторно использовать его операции - PullRequest
0 голосов
/ 18 февраля 2020

В качестве упражнения я попытался создать простой класс 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) - что я не так делаю?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...