开发者

How can two coupled Scala generic type constructors refer to each other as type parameters?

In Java 1.6.0_21, the first example below compiles fine, and I think that's because the parameter type bounds are bare. That is, in the "Z extends Zen" bound below, Java allows Zen to slide by as the name of a raw, non-generic type (equivalent to the runtime "erased" type). This might be wrong and bad, but it also can be useful, or at least wacky good times on the bus ride home:

public class CorefTest {

    public static interface Tao<Z extends Zen> {
    }

    public static interface Zen<T extends Tao> {
    }
}

In Scala 2.8.0.final, the below compiles great up through CleanOceanWithFish, showing some basic type param hookups. But when we get to Zen and Tao being interdependent generic types, the Scala compiler rejects my weaving construct. See compiler errors in comments.

package heaven.piece
class Lucky {
    trait Water {}
    trait CleanWater extends Water {}
    trait Sea [W <: Water] {}
    trait Fish[S <: Sea[CleanWater]] {}

    trait CleanOceanWithFish[F <: Fish[CleanOceanWithFish[F]]] 
                   extends Sea[CleanWater]{}

// Above code compiles fine, but the type constructor pair below doesn't compile 

    trait Tao[Z <: Zen[Tao[Z]]]{};

    trait Zen[T <: Tao[Zen[T]]]{};
}
// error: type arguments [Lucky.this.Tao[Z]] do not conform to trait Zen's 
//     type parameter bounds [T <: Lucky.this.Tao[Lucky.this.Zen[T]]]

// error: type arguments [Lucky.this.Zen[T]] do not conform to trait Tao's 
//     type parameter bounds [Z <: Lucky.this.Zen[Lucky.this.Tao[Z]]]

So, how can I properly tie the Scala (2.8.0) knot between Tao and Zen?

It's certainly a contrived example, but what I really want is to use Scala to extend some real Java types I have working in the above form (which existential types via "forSome" and "[_]" are not helping me with, so far). I think that getting Zen and Tao compiling in Scala will probably show the way to that Java extension. If you can consider the Java extension issue in your answer, so much the better. Thanks for any help!

Update posted after the very helpful first two answers below from Nikita S. and Kris N.

I empirically learned some more about various Java+Scala coreference scenarios. The upshot is that when we want interoperable coreferent types in both Java and Scala, then this Java construct:

public static interface JavaFunTao<JFZ extends JavaFunZen<? extends JavaFunTao<JFZ>>> {
    public JFZ consider(JFZ someZen, JavaFunTao<JFZ> otherTao);
}
public static interface JavaFunZen<JFT extends JavaFunTao<? extends JavaFunZen<JFT>>> { 
    public JFT meditate(JFT someTao, JavaFunZen<JFT> otherZen);
}

provides more specific typing than my first Java example at top (avoiding the raw types), and is then properly extensible in Scala as follows:

class HiFunTao[HFZ <: HiFunZen[  _ <: HiFunTao [HFZ]]] extends JavaFunTao[ HFZ] {
    override def consider(someZen: HFZ, otherTao: JavaFunTao[HFZ]) : HFZ = {
        println (this.toString() + " is considering " + someZen + " and " + otherTao);
        someZen
    }
}
class HiFunZen[HFT <: HiFunTao[ _ <:  HiFunZen [HFT]]] extends JavaFunZen[ HFT] {
    override def meditate(someTao: HFT, otherZen: JavaFunZen[HFT]) : HFT = {
        println (this.toString() + " is meditating on " + someTao + " and " + otherZen);
        someTao
    }
}

I verified that we can make simple concrete types based on these, instantiate them, and invoke their methods. The key step in both the Java and the Scala is placing the bounded wildcard at the point where the type parameter tr开发者_如何学Cee loops back into the current declaration type, i.e. the "? extends" in java, and the "_ <:" in Scala.


Since neither Zen nor Tao are covariant, there's a substitutability problem in your zen and tao. This compiles fine for me under 2.8.1:

trait Tao[+Z <: Zen[Tao[Z]]]

trait Zen[+T <: Tao[Zen[T]]]

Of course, if you want to meditate on Z or T then this is also not going to work for you, at least not exactly as stated because of the issue of having a covariant type parameter in a contravariant position. You can get around that problem like this:

trait Tao[+Z <: Zen[Tao[Z]]] {
  def meditate[M >: Z](m: M) = ()
}

trait Zen[+T <: Tao[Zen[T]]] {
  def meditate[M >: T](m: M) = ()
}

but this may still produce constraints on your implementation that you may not want. It's confusing, at the least. :)

Update:

This also, by the way, avoids the problem mentioned in the update of NSkvortsov's answer. This compiles fine:

class Zazen extends Zen[Tao[Zazen]]


I think, this is what you need:

class MyClass {

  trait Tao[Z <: Zen[_]]{};
  trait Zen[T <: Tao[_]]{};

}

This snippet successfully compiled by Scala 2.8.1

Update

Unfortunately, extending CorefTest.Zen and CorefTest.Tao in scala is impossible. Here is why.

The only way to implement interfaces in java is to use raw types:

public class TaoImpl<Z extends CorefTest.Zen> implements CorefTest.Tao<Z> { }

public class ZenImpl<T extends CorefTest.Tao> implements CorefTest.Zen<T> { }

than one can instantiate classes like this:

TaoImpl<ZenImpl> tao = new TaoImpl<ZenImpl>();
ZenImpl<TaoImpl> zen = new ZenImpl<TaoImpl>();

But scala does not support raw types. So TaoImpl and ZenImpl just can not be defined. Please see this e-mail thread and issues #2091 and #1737 for detailed discussion

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜