2

I'm quite new to Scala (2.8) and here's something that I'm struggling to express in Scala:

I need to define a class that (due to interoperability with a Java library) implements Comparable; its generic type has to be Comparable with itself or a superclass

I also need to have a no-args constructor along with another that makes use of the generic parameter

I wrote a simple equivalent of what I'm trying to get in Java:

public class MyComparable<T extends Comparable<? super T>>{
    public MyComparable() {}

    public MyComparable(T a){
        System.out.println(a);
    }
}

I can import without any problem this class in the scala REPL and instantiate it.

This is what I'm writing in Scala to try to accomplish the same thing:

import java.lang.Comparable

class MyComparable[T <: Comparable[_>:Tb],Tb]()(implicit ev: T=:=Tb) {
    def this(a: T) = {
        this()
        println(a)
    }
}

I tried both by using the no-args constructor as the default one, or using the one with the T argument: in both cases I get error: could not find implicit value for parameter ev: =:=[T,Tb] at line 5

Afaik, =:= is imported by default from scala.Predef (and in fact, this code works fine with only one constructor)

berdario
  • 1,851
  • 18
  • 29
  • What was your original problem? That `class MyComparable[T <: Ordered[_>:T]]` was rejected? Your Scala code hasn't conceptually that much to do with the example Java code you have shown above. – soc Mar 17 '11 at 11:36
  • My original problem involves 2 generics classes like this, with accessors/mutators, 2 times the generic parameters... overall is much much more bulky and messy, I can paste it somewhere else, but I've reduced my problem to these few lines, and I don't think that using my original code would add anything to this question – berdario Mar 17 '11 at 11:58
  • I don't know what do you mean by "rejected" but obviously you can't write the signature that way or you'll get a cyclic reference. I fail to see why you say that the Scala code would be conceptually different from the Java code... do you care to elaborate the differences? – berdario Mar 17 '11 at 12:01
  • (adding instead of editing due to the 5 minutes limit) @soc I mean: both have a generic parameter that has to be COmparable with its same type or a supertype, both have an empty constructor, both have a constructor with the generic parameter T I actually left a System.out.println(a) in the Java code that's missing from my Scala code, that was the only difference afaik, and now I've fixed it – berdario Mar 17 '11 at 12:10

3 Answers3

3

Not entirely certain what you're trying to do, but you can do it like this:

import java.lang.Comparable

class MyComparable[T <: Comparable[_>:Tb],Tb]()(implicit ev: T=:=Tb) {
  def this(a: T)(implicit ev: T=:=Tb) = {
    this()
    println(a)
  }
}
Submonoid
  • 2,809
  • 2
  • 20
  • 25
  • Thanks! I'm still quite unused to Scala's constructors, and multiple parameters list, so I haven't thought before of duplicating the implicit parameter down on the other constructor (you know... being `implicit` deceived me into thinking it wasn't needed) still: the Java equivalent is more convenient: I can instantiate the Scala MyComparable like this: `new MyComparable[String,String]()` `new MyComparable[String,String]("dummy")` – berdario Mar 17 '11 at 17:50
  • while the Java MyComparable can be used like this: `new MyComparable()` `new MyComparable("dummy")` `new MyComparable[String]()` `new MyComparable[String]("dummy")` Thus, for now I'll upvote the answer and wait to see if there're better solutions (It's my first question here on SO, maybe it's quite superfluous to state this) edit: apparently I can't upvote until I'll have 15 reputation :/ – berdario Mar 17 '11 at 17:52
  • I accepted your answer for these reasons: - I don't like to accept my own answer - Your answer is the closest to my original approach - I really need the +2 rep for adding extra links to my answer (ugh: I hate this site) – berdario Apr 04 '11 at 22:34
3

You are over-complicating the issue -- though it would be nice if Java had declaration-site variance, which would make all of this moot. Anyway, here's the equivalent code:

class MyComparable[T <: Comparable[T2], T2 >: T]() {
    def this(a: T) = {
        this()
        println(a)
    }
}

Granted that this does not use raw type, and has two type parameters instead of one. Then again, there's the question of what you are actually trying to accomplish with that declaration. I wonder if what you actually want isn't this:

import scala.annotation.unchecked.uncheckedVariance
class MyComparable[-T <: Comparable[T @uncheckedVariance]]() {
    def this(a: T) = {
        this()
        println(a)
    }
}

I tell Scala to ignore variance above at my peril, because I assume Comparable can, indeed, be contra-variant. The following code indicates that's indeed the case:

scala> trait Cp[-T] {
     |   def compareTo(other: T): Int
     | }
defined trait Cp

scala> class MyComparable[-T <: Cp[T]] {
     | }
defined class MyComparable
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • Thanks, your first proposal is very simple, though it forces us to explicitly specify the T2 type of which our T is a subtype. The second proposal is Interesting, but I tried in my case and indeed it doesn't help... I pondered on it quite a bit, and I think that exploting contravariance doesn't help here: the problem is with the type that's passed to the constructor; having a contravariant parameter might help only when fetching that type from the class, or reassigning the object to some other sub/superclass – berdario Apr 04 '11 at 22:53
  • Actually, before posting this question, I didn't even know about the meaning of covariance and contravariance in scala, but since I've the burden of working with plain old java invariants, my options were already quite limited. Your third example is neat and (together with snippet #2) prompted me to learn how does variance work in scala, but it's still unusable in this case: my class has to be able to accept existing java types that can't implement a Cp scala trait (on second thought, I could subclass all the needed classes and mixin this trait for each one, but it seems quite dirty this way) – berdario Apr 04 '11 at 23:05
2

This still isn't the same as the Java code I posted, but it seems to be close enough and cleaner than the other solutions:

import java.lang.Comparable
class MyComparable[T <: Comparable[T]]() {
    def this(a: Comparable[_ >: T]) = {
        this()
        println(a)
    }
}

Here's a bunch of miscellaneous links I read since when I got stuck with this problem, that may help someone else with a similar problem:

When is @uncheckedVariance needed in Scala, and why is it used in GenericTraversableTemplate?

to understand what does =:= really do

http://programming-scala.labs.oreilly.com/ch12.html#VarianceUnderInheritance

http://www.scala-lang.org/node/129

Type erasure and other low-level details of the Java generics

http://www.scala-lang.org/node/124

http://scalada.blogspot.com/2008/01/existential-types.html

http://lamp.epfl.ch/~emir/bqbase/2007/06/13/existentials.html

Community
  • 1
  • 1
berdario
  • 1,851
  • 18
  • 29