Re: On inference of existential types

Andrew Phillips <[email protected]> Sat, 21 May 2016 11:13:02 -0700 (PDT)
Newsgroups gmane.comp.lang.scala
Message-ID <[email protected]>
Hi Guanpeng!

The second part of your example - instantiating a Q of Q - compiles in 
Scala for me (2.11.6) if we simply let the compiler figure out the types:

scala> val q = new Q(new Q())
q: Q[Nothing] = Q@3294fb9d

scala> val b = Q.g(q) // works
java.lang.UnsupportedOperationException
  at Q.g(Q.java:22)
  ... 33 elided

scala> val q2: Q[Q[Int]] = new Q(new Q()) // now with Int
q2: Q[Q[Int]] = Q@27606b76

scala> val b = Q.g(q2)
java.lang.UnsupportedOperationException
  at Q.g(Q.java:22)
  ... 33 elided

The variant with the array is a little trickier example because it's 
difficult to compare directly to Java, since Java doesn't support generic 
arrays. The comparison is more direct if we change the Java class to use 
types that support generics in Java, too, e.g.

    public static <T> Q<T> fprime(List<Q<? extends T>> qs) {
        throw new UnsupportedOperationException();
    }

    public static <T, S extends T> Q<T> fdoubleprime(List<Q<S>> qs) {
        throw new UnsupportedOperationException();
    }

With this example, we can get fdoubleprime to compile by letting the 
compiler figure out the type variables for T and S:

scala> import collection.JavaConverters._

scala> val qs: Seq[Q[Int]] = Seq(new Q())
qs: Seq[Q[Int]] = List(Q@11cb7a27)

scala> val a2 = Q.fdoubleprime(qs.asJava)
java.lang.UnsupportedOperationException
  at Q.fdoubleprime(Q.java:18)
  ... 33 elided

The fprime bit is trickier, though. Here, Scala seems to need a type hint, 
where Java doesn't:

scala> val qs: Seq[Q[_ <: Int]] = Seq(new Q())
qs: Seq[Q[_ <: Int]] = List(Q@ea66840)

scala> val a = Q.fprime(qs.asJava) // fails to compile

scala> val a: Q[Int] = Q.fprime(qs.asJava) // fails to compile

scala> val a = Q.fprime[Int](qs.asJava) // works
java.lang.UnsupportedOperationException
  at Q.fprime(Q.java:14)
  ... 33 elided

This differs from Java, which *does* allow the equivalent call to compile:

    public static void callFprime() {
        List<Q<? extends Integer>> qs = new ArrayList<Q<? extends 
Integer>>();
        fprime(qs);
    }

The hint is also required if we define the equivalent method in Scala:

scala> def fprimeInScala[T](qs: Seq[Q[_ <: T]]): Q[T] = ???
fprimeInScala: [T](qs: Seq[_ <: T])Q[T]

val a3 = fprimeInScala(qs) // fails to compile

val a3: Q[Int] = fprimeInScala(qs) // fails to compile

scala> val a3 = fprimeInScala[Int](qs) // works
scala.NotImplementedError: an implementation is missing
  at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225)
  at .fprimeInScala(<console>:10)
  ... 33 elided

We can call the Scala method successfully without the explicit type hint if 
we drop the wildcard type:

scala> val qs2: Seq[Q[Int]] = Seq(new Q()) // no wildcard
qs2: Seq[Q[Int]] = List(Q@68461613)

scala> val a3 = fprimeInScala(qs2) // works
scala.NotImplementedError: an implementation is missing
  at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225)
  at .fprimeInScala(<console>:10)
  ... 33 elided

An interesting note is that the equivalent call to Java does not work 
since, unlike Seq, java.util.List is treated as invariant. So while a 
Seq[Q[Int]] is a Seq[Q[_ <: Int]] since Int <: Int, a List[Q[Int]] is *not* a 
List[Q[_ <: Int]]:

scala> val a4 = Q.fprime(qs2.asJava)
<console>:11: error: type mismatch;
 found   : java.util.List[Q[Int]]
 required: java.util.List[Q[_]]
Note: Q[Int] <: Q[_], but Java-defined trait List is invariant in type E.
You may wish to investigate a wildcard type such as `_ <: Q[_]`. (SLS 
3.2.10)
       val a4 = Q.fprime(qs2.asJava)
                             ^

I'm afraid I don't have an answer as to whether the Scala compiler should 
be able to "introduce" the extra type variable S = _ <: T that is the 
difference between fprime and fdoubleprime, and use it to solve the type 
equation - would also be curious to learn more about that.

Regards

ap

-- 
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
For more options, visit https://groups.google.com/d/optout.