开发者

How to override a generic method with implicit arguments if the generic type is already fixed?

开发者 https://www.devze.com 2023-04-02 11:10 出处:网络
I try to override this method def sum[B >: A](implicit num: Numeric[B]): B = ... in a subclass where type A is already fixed to Int.

I try to override this method

def sum[B >: A](implicit num: Numeric[B]): B = ...

in a subclass where type A is already fixed to Int.

I already tried

override def sum: Int = ...

but this doesn't override of course, leading to different method resolution based on the dynamic type at runtime.

Going further,

def sum[B >: Int](implicit num: Numeric[B]): Int

does override, while

def sum[B >: Int](implicit num: Numeric[Int]): Int

does not, as well as

def sum(implicit num: Numeric[Int]): Int

Why is that the case? Is it at leats possible to get rid of the superfluous bound B?

I'm not sure which types and implicits I can leave out and what has to stay so that the method still o开发者_StackOverflowverrides.


The first problem is that overridden methods need the same number and kind of type parameters, even if they are not used. For example,

class C1 {
  def f[B] = println("hello")
}

class C2 extends C1 {
  override def f = println("world") // Error: f overrides nothing (needs parameter B)
}

Beyond this, there's also a problem with soundness since Numeric[A] is invariant in A. This means there is no subtype relationship between Numeric[B] and Numeric[C] whenever B and C are different.

Imagining that Numeric were a trait with the proper variance, it still wouldn't work; apparently, the signature of overridden methods needs to be exactly the same:

class D1 {
  def g(x: Int) {}
}

class D2 extends D1 {
  override def g(x: Any) {} // Error: g overrides nothing (different parameter type)
}

I'm not sure why the types of overridden methods can't be widened. Edit: Perhaps the reason is compatibility with overloading, as in

class D1 {
  def g(x: Any) {}
  def g(x: Int) {} // This is legal, even though the definitions seem to overlap
}

To summarize, when you override, you must preserve the signature of the method, including the type parameters. In your case, this is about the best you can do:

override def sum[B >: Int](implicit num: Numeric[B]): B = ...


Ok, trying to explain why the rules must force you to keep the signature with implicit parameter and variance.

First, animplicit argument is still an argument, it can be passed explicitely, and except maybe when it has a singleton type (which would not be very useful), several different instances of it are possible.

Suppose I create

case class ZModulo(val p: Int) extends Numeric[Int] {
  def plus(a: Int, b: Int) = (a+b) % p
  // others along the same line
}

It seems like a proper Numeric. Numeric documentation does not say which laws should be expected, but ZModulo is not unreasonable.

Now there is your

class Summable[A] {
  def sum[B >: A](implicit num: Numeric[A]): B =...
}

If I have val ints : Summable[Int], I am certainly allowed to call ints.Sum(ZModulo(3)). So if your class is to be a subclass of Summable[Int], it must allow me that. So you cannot remove the Numeric parameter.

Second, suppose I come with a Numeric[Any]. Not sure how I could do that reasonably for a numeric, but the spec and the compiler can't know that. And anyway, they must accept unreasonable implementations too. So let's have

object MixThemAll : Numeric[A] {...}

The signature in Summable allows ints.sum(MixThemAll). So your subclass must allow that too.

So letting you remove either implicit parameter or variance in the subclass would be unsound.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号