bug icon indicating copy to clipboard operation
bug copied to clipboard

Type inference involving wildcards and SAMs

Open scabug opened this issue 9 years ago • 5 comments

> console
[info] Starting scala interpreter...
Welcome to Scala 2.12.0-M4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_77).
Type in expressions for evaluation. Or try :help.

scala> import java.util.stream.Collectors
import java.util.stream.Collectors

scala> import scala.collection.JavaConverters._
import scala.collection.JavaConverters._

scala> 1.to(10).asJava.stream().map(_ + 1).collect(Collectors.toList[Int])
<console>:26: error: type mismatch;
 found   : java.util.stream.Collector[Int,?0(in value res0),java.util.List[Int]] where type ?0(in value res0)
 required: java.util.stream.Collector[_ >: ?0(in value x$1), ?, ?]
Note: Int <: Any, but Java-defined trait Collector is invariant in type T.
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
       1.to(10).asJava.stream().map(_ + 1).collect(Collectors.toList[Int])
                                                                    ^

scabug avatar Apr 15 '16 15:04 scabug

Imported From: https://issues.scala-lang.org/browse/SI-9756?orig=1 Reporter: @hseeberger Affected Versions: 2.12.0-M4

scabug avatar Apr 15 '16 15:04 scabug

@SethTisue said: Note that the problem is in the map call, the compiler never even reaches the collect at the end. And it isn't an Int vs Integer thing, the same thing happens with e.g. String.

One workaround is to explicitly annotate the return type in the call to map:

scala> 1.to(10).asJava.stream().map[Int](_ + 1).collect(Collectors.toList[Int])
res3: java.util.List[Int] = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
scala> Seq("foo").asJava.stream().map[String](_.reverse)
res4: java.util.stream.Stream[String] = java.util.stream.ReferencePipeline$3@69d2c460

But note that java.util.List[Int] isn't really a proper type since Java doesn't allow primitive types as type parameters. What type you might want instead depends on what you're doing.

A more generally applicable workaround here is to use scala-java8-compat (https://github.com/scala/scala-java8-compat), which exists exactly to improve interop in situations like this:

scala> import scala.compat.java8.StreamConverters._
scala> 1.to(10).seqStream.map(_ + 1).toScala[Vector]
res0: Vector[Int] = Vector(2, 3, 4, 5, 6, 7, 8, 9, 10, 11)

scala> import java.util.stream.Collectors
scala> 1.to(10).seqStream.map(_ + 1).boxed.collect(Collectors.toList())
res1: java.util.List[Integer] = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

and so on.

scabug avatar Apr 19 '16 19:04 scabug

@szeiger said: This looks like a bug with the SAM type inference. Compare:

scala> 1.to(10).asJava.stream().map(_ + 1)
res7: java.util.stream.Stream[?0] = java.util.stream.ReferencePipeline$3@2c991465

scala> 1.to(10).asJava.stream().map[Int](_ + 1)
res8: java.util.stream.Stream[Int] = java.util.stream.ReferencePipeline$3@f171912

Here's the signature of Stream.map:

<R> Stream<R> 	map(Function<? super T,? extends R> mapper)

It looks like the existential type from the call-site variance on R leaks out into the return type instead of being replaced by the inferred type R = Int.

scabug avatar Apr 20 '16 12:04 scabug

@adriaanm said: The problem is that without definition-site variance, it's not sound to replace ? extends R with R. We could try to be smarter in type inference and basically infer the variance of type parameters of java-defined types, so that we can extrapolate the bounded wildcard away to its bound, but that's pretty tricky. Let's experiment in the 2.12.x series, but I won't be able to fix this for 2.12.0

scabug avatar Jun 28 '16 18:06 scabug

When call java.util.concurrent.CompletionStage#handle in scala 2.12.16 [error] --- because --- [error] argument expression's type is not compatible with formal parameter type; [error] found : java.util.function.BiFunction[T,Throwable,Unit] [error] required: java.util.function.BiFunction[_ >: T, Throwable, _ <: ?U] [error] Note: T <: Any, but Java-defined trait BiFunction is invariant in type T. [error] You may wish to investigate a wildcard type such as _ <: Any. (SLS 3.2.10) [error] stage.handle((value: T, ex: Throwable) => { [error] ^

Have to insert the Unit

          stage.handle[Unit]((value, ex) => {
            if (ex != null) {
              callback(scala.util.Failure[T](ex))
            } else {
              callback(scala.util.Success(value))
            }
          })

He-Pin avatar Oct 05 '22 08:10 He-Pin