Как переопределить общий метод с неявными аргументами, если общий тип уже исправлен?

Я пытаюсь переопределить этот метод

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 = ...