Search code examples
scalacomparable

How to minimally reconcile Scala's @specialized and java.lang.Comparable?


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)


Solution

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