I have a third party Java API with methods like this (simplified) :
public <C extends Comparable<C>> C jFoo(C c);
I want to write a wrapper like this:
def foo[C <: Comparable[C]](c: C): C = jFoo(c)
For allowing the wrapper method to accept primitives (since, to my understanding, boxing will happen anyway so that's not relevant), I want to specialize C
:
def foo[@specialized(Int, Double) C <: Comparable[C]](c: C): C = jFoo(c)
However, scalac complains that, due to the upper bound on the parameter, the method cannot be specialized.
A solution would be to add an evidence parameter:
def foo[@specialized(Int, Double) C, CC <: Comparable[CC]](c: C)
(implicit evComp: C => CC): CC =
jFoo(evComp(c))
This works, and you can now call:
foo(2.0)
However, the method signature - part of the API - is not really readable now.
Is there an alternative approach at the method level that would produce an equivalent, more readable signature, while maintaining specialization?
(Scala 2.12.x is fine, Dotty not so much)
You could get rid of the second type parameter CC
by making it a type member of a special EvC
type class.
The following works:
import java.lang.Comparable
def jFoo[C <: Comparable[C]](c: C): Unit = println("compiles => ship it")
trait EvC[C] {
type CC <: Comparable[CC]
def apply(c: C): CC
}
def foo[@specialized(Int, Double) C](c: C)(implicit evC: EvC[C]): Unit =
jFoo[evC.CC](evC(c))
implicit object DoubleEvC extends EvC[Double] {
type CC = java.lang.Double
def apply(c: Double) = (c: java.lang.Double)
}
foo(42.0) // compiles => ship it
In a sense, I've just glued together the C => CC
part with the type argument CC
itself, so that the type CC
does no longer appear in the type parameter list. I'm not sure whether you can get rid of all of that completely, it's still a fact that the built-in primitive types do not implement java.lang.Comparable
.