Как переопределить общий метод с неявными аргументами, если общий тип уже исправлен?
Я пытаюсь переопределить этот метод
def sum[B >: A](implicit num: Numeric[B]): B = ...
в подклассе, где тип A
уже привязан к Int
.
Я уже пробовал
override def sum: Int = ...
но это, конечно, не отменяет, что приводит к разному разрешению метода на основе динамического типа во время выполнения.
Далее,
def sum[B >: Int](implicit num: Numeric[B]): Int
переопределяет, а
def sum[B >: Int](implicit num: Numeric[Int]): Int
а также
def sum(implicit num: Numeric[Int]): Int
Почему это так? Можно ли на лиатах избавиться от лишней привязки B
?
Я не уверен, какие типы и имплициты я могу оставить и что должно остаться, чтобы метод все еще переопределял.
Ответы
Ответ 1
Хорошо, пытаясь объяснить, почему правила вынуждают вас хранить подпись с неявным параметром и дисперсией.
Во-первых, animplicit аргумент по-прежнему является аргументом, он может передаваться явно, и, возможно, когда он имеет одноэлементный тип (что было бы не очень полезно), возможны несколько разных экземпляров.
Предположим, что я создаю
case class ZModulo(val p: Int) extends Numeric[Int] {
def plus(a: Int, b: Int) = (a+b) % p
// others along the same line
}
Кажется правильным Numeric
. Документация Numeric
не говорит, какие законы следует ожидать, но ZModulo
не является необоснованным.
Теперь есть ваш
class Summable[A] {
def sum[B >: A](implicit num: Numeric[A]): B =...
}
Если у меня есть val ints : Summable[Int]
, мне позволено называть ints.Sum(ZModulo(3))
. Поэтому, если ваш класс должен быть подклассом Summable[Int]
, он должен мне это позволить. Поэтому вы не можете удалить параметр Numeric
.
Во-вторых, предположим, что я пришел с Numeric[Any]
. Не уверен, как я могу сделать это разумно для числового, но спецификация и компилятор не могут этого знать. И вообще, они должны принять необоснованные реализации. Итак, пусть
object MixThemAll : Numeric[A] {...}
Подпись в Summable позволяет ints.sum(MixThemAll)
. Таким образом, ваш подкласс должен это допускать.
Так что позволить вам удалить неявный параметр или дисперсию в подклассе, будет необоснованным.
Ответ 2
Первая проблема заключается в том, что переопределенным методам требуется одинаковое количество и тип параметров типа, даже если они не используются. Например,
class C1 {
def f[B] = println("hello")
}
class C2 extends C1 {
override def f = println("world") // Error: f overrides nothing (needs parameter B)
}
Кроме того, также существует проблема со звуком, поскольку Numeric[A]
инвариантна в A
. Это означает, что между Numeric[B]
и Numeric[C]
нет отношения подтипа, когда B
и C
отличаются.
Представляя, что Numeric
является признаком правильной дисперсии, он все равно не сработает; по-видимому, подпись переопределенных методов должна быть точно такой же:
class D1 {
def g(x: Int) {}
}
class D2 extends D1 {
override def g(x: Any) {} // Error: g overrides nothing (different parameter type)
}
Я не уверен, почему типы переопределенных методов не могут быть расширены. Изменить: Возможно, причиной является совместимость с перегрузкой, как в
class D1 {
def g(x: Any) {}
def g(x: Int) {} // This is legal, even though the definitions seem to overlap
}
Подводя итог, при переопределении вы должны сохранить подпись метода, включая параметры типа. В вашем случае это самое лучшее, что вы можете сделать:
override def sum[B >: Int](implicit num: Numeric[B]): B = ...