Почему 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.