Почему Numeric ведет себя иначе, чем Ordered?

Scala имеет ряд признаков, которые можно использовать в качестве классов классов, например Ordered и Numeric в пакете scala.math.

Я могу написать, например, общий метод с помощью Ordered следующим образом:

def f[T <% Ordered[T]](a: T, b: T) = if (a < b) a else b

Я хотел сделать аналогичную вещь с Numeric, но это не работает:

def g[T <% Numeric[T]](a: T, b: T) = a * b

Почему существует явное несоответствие между Ordered и Numeric?

Я знаю, что есть другие способы сделать это, следующее будет работать (использует привязку к контексту):

def g[T : Numeric](a: T, b: T) = implicitly[Numeric[T]].times(a, b)

Но это выглядит сложнее, чем просто использовать * для умножения двух чисел. Почему черта Numeric не включает такие методы, как *, а Ordered включает такие методы, как <?

Я также знаю Ordering, который можно использовать так же, как Numeric, см. также этот ответ:

def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b)

Ответ 1

Ordered - это всего лишь несколько простых сутенертных методов, которые возвращают либо Int, либо Boolean, поэтому не требуется никакого обмана типа.

Numeric, с другой стороны, имеет методы, которые возвращают разные типы в зависимости от используемого точного подкласса. Таким образом, хотя Ordered немного больше, чем признак маркера, Numeric является полнофункциональным классом типа.

Чтобы вернуть операторов, вы можете использовать mkNumericOps (определенный в Numeric) в операнде lhs.

UPDATE

Miles совершенно прав, mkNumericOps неявно, поэтому просто импортировать этот экземпляр Numeric вернет вам все волшебство...

Ответ 2

Символьные операторы доступны, если вы импортируете их из неявного числа [T]

def g[T : Numeric](a: T, b: T) = {
  val num = implicitly[Numeric[T]]
  import num._
  a * b
}

Это явно немного громоздко, если вы хотите использовать только один оператор, но в нетривиальных случаях накладные расходы на импорт не так уж хороши.

Почему операторы недоступны без явного импорта? Обычные соображения против создания имплицитов, видимых по умолчанию, применяются здесь, возможно, более того, потому что эти операторы настолько широко используются.

Ответ 3

Вы можете уменьшить решение Miles, чтобы использовать только одну дополнительную строку:

Добавьте неявное преобразование из A : Numeric в Numeric[A]#Ops

object Ops {
  implicit def numeric[A : Numeric](a: A) = implicitly[Numeric[A]].mkNumericOps(a)
}

Затем примените это значение в свой метод

def g[T : Numeric](a: T, b: T) = {
  import Ops.numeric
  a * b
}

Подробнее см. Scala билет 3538.