Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CollectionConverters conversion between java.lang.Long and scala.Long #16349

Open
DieBauer opened this issue Nov 15, 2022 · 6 comments
Open

CollectionConverters conversion between java.lang.Long and scala.Long #16349

DieBauer opened this issue Nov 15, 2022 · 6 comments
Labels

Comments

@DieBauer
Copy link
Contributor

DieBauer commented Nov 15, 2022

Compiler version

Scala compiler version 3.2.1 -- Copyright 2002-2022, LAMP/EPFL

Minimized code

create java file in ./test/L.java

package test;

import java.util.Map;

public class L {

    public static Long y() {
        return 123L;
    }

    public static Map<String, Long> maps() {
        return Map.of("A", 1L);
    }
}

create following scala file in ./Call.scala

import test._

class Call {

  def x(): Long = L.y()

  import scala.jdk.CollectionConverters._

//  def mmap(): Map[String, Long] = L.maps().asScala.toMap
  def maps(): Map[String, Long] = L.maps().asScala.toMap.collect { case (k, v) if v > 0L => (k, v)}

}

Run the following:

javac -d classes test/L.java
scalac -classpath ./classes Call.scala

Output

-- [E007] Type Mismatch Error: Call.scala:9:65 ---------------------------------
9 |  def maps(): Map[String, Long] = L.maps().asScala.toMap.collect { case (k, v) if v > 0L => (k, v)}
  |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |                Found:    PartialFunction[(String, Long), (String, Long)]
  |                Required: PartialFunction[(String, Long), (String, Long²)]
  |
  |                where:    Long  is a class in package java.lang
  |                          Long² is a class in package scala
  |
  | longer explanation available when compiling with `-explain`
2 errors found

Expectation

Scala compiler can convert java.lang.Long to scala.Long, just as the case with the single statement in the example file.

@DieBauer DieBauer added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Nov 15, 2022
@jchyb jchyb added itype:enhancement area:implicits related to implicits and removed itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Nov 16, 2022
@odersky
Copy link
Contributor

odersky commented Nov 19, 2022

Implicit conversions are only applied at the top-level. If there is an implicit conversion scala.Long to java.lang.Long this does not mean there is also an implicit conversion from PartialFunction[X, (String, scala.Long)] to PartialFunction[X, (String, java.lang.Long)].

@odersky odersky closed this as completed Nov 19, 2022
@DieBauer
Copy link
Contributor Author

Thanks for the reply!
My main issue is that this line

  def maps(): Map[String, Long] = L.maps().asScala.toMap.collect { case (k, v) if v > 0L => (k, v)}

does compile with Scala 2.13.10, but fails with the above error in Scala 3.x.
So in my codebase I have to call v.longValue to make it compile now.

I created this issue to understand if 3.x is more correct, of if 2.13 is wrong to apply the conversion.

@lrytz
Copy link
Member

lrytz commented Nov 24, 2022

@DieBauer I get the same error with Scala 2.13.10, can you check again?

Test.scala:9: error: Cannot prove that (String, Long) <:< (String, Long).
  def mmap(): Map[String, Long] = L.maps().asScala.toMap
                                                   ^

@DieBauer
Copy link
Contributor Author

Correct! That was a mistake from my side to include that statement. The fact is that the second approach, with collect ensures that this can be converted.

import test._

class Call {

  def x(): Long = L.y()

  import scala.jdk.CollectionConverters._

  def maps(): Map[String, Long] = L.maps().asScala.toMap.collect { case (k, v) if v > 0L => (k, v)}

}

Could you see that this does compile with 2.13.10 and fails with 3.2.1?

@lrytz
Copy link
Member

lrytz commented Nov 24, 2022

Ah, thanks. Yes, that example compiles with 2.13.10, and compilation fails with 3.2.1.

-- [E007] Type Mismatch Error: A.scala:9:65 ------------------------------------
9 |  def maps(): Map[String, Long] = L.maps().asScala.toMap.collect { case (k, v) if v > 0L => (k, v)}
  |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |                Found:    PartialFunction[(String, Long), (String, Long)]
  |                Required: PartialFunction[(String, Long), (String, Long²)]
  |
  |                where:    Long  is a class in package java.lang
  |                          Long² is a class in package scala

@lrytz lrytz reopened this Nov 24, 2022
@DieBauer
Copy link
Contributor Author

I diffed the output after the typer phase for both scala 2 and 3, and in scala 2 the value type (java Long) gets an implicit conversion applied scala.Tuple2.apply[String, Long](k, scala.Predef.Long2long(v)). While in Scala 3 that doesn't happen Tuple2.apply[String, Long](k, v), even though the guard on the value 0L does get the conversion applied.

Scala 3 output

package <empty> {
  class B() extends Object() {
    import scala.jdk.CollectionConverters.*
    def maps(): Map[String, Long] = 
      jdk.CollectionConverters.MapHasAsScala[String, Long](L.maps()).asScala.
        toMap
      [String, Long](<:<.refl[(String, Long)]).collect[String, Long](
        {
          def $anonfun(x$1: (String, Long)): (String, Long) = 
            x$1:(x$1 : (String, Long)) @unchecked match 
              {
                case Tuple2.unapply[String, Long](k @ _, v @ _) if 
                  Long2long(v).>(0L)
                 => 
                  Tuple2.apply[String, Long](k, v)
              }
          closure($anonfun:PartialFunction[(String, Long), (String, Long)])
        }
      )
  }
}

Scala 2 output

package <empty> {
  class B extends scala.AnyRef {
    def <init>(): B = {
      B.super.<init>();
      ()
    };
    import scala.jdk.CollectionConverters._;
    def maps(): Map[String,Long] = scala.jdk.CollectionConverters.MapHasAsScala[String, Long](L.maps()).asScala.toMap[String, Long](scala.this.<:<.refl[(String, Long)]).collect[String, Long](({
      @SerialVersionUID(value = 0) final <synthetic> class $anonfun extends scala.runtime.AbstractPartialFunction[(String, Long),(String, Long)] with java.io.Serializable {
        def <init>(): <$anon: ((String, Long)) => (String, Long)> = {
          $anonfun.super.<init>();
          ()
        };
        final override def applyOrElse[A1 <: (String, Long), B1 >: (String, Long)](x1: A1, default: A1 => B1): B1 = ((x1.asInstanceOf[(String, Long)]: (String, Long)): (String, Long) @unchecked) match {
          case (_1: String, _2: Long): (String, Long)((k @ _), (v @ _)) if scala.Predef.Long2long(v).>(0L) => scala.Tuple2.apply[String, Long](k, scala.Predef.Long2long(v))
          case (defaultCase$ @ _) => default.apply(x1)
        };
        final def isDefinedAt(x1: (String, Long)): Boolean = ((x1.asInstanceOf[(String, Long)]: (String, Long)): (String, Long) @unchecked) match {
          case (_1: String, _2: Long): (String, Long)((k @ _), (v @ _)) if scala.Predef.Long2long(v).>(0L) => true
          case (defaultCase$ @ _) => false
        }
      };
      new $anonfun()
    }: PartialFunction[(String, Long),(String, Long)]))
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants