Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why warning when scala.language.implicitConversions is not the last import?

In my Scala code, I have some implicit conversion, and I have the necessary import present:

import scala.language.implicitConversions

However, sometimes when there is another import done after this one, I get the warning as if the import is not there at all:

Warning:(112, 18) implicit conversion method pair2Dimension should be enabled by making the implicit value scala.language.implicitConversions visible.

build.sbt:

name := "ImplicitSBT"

version := "1.0"

scalaVersion := "2.11.5"

scalacOptions ++= Seq("-deprecation","-feature")

libraryDependencies += "org.scala-lang.modules" %% "scala-swing" % "1.0.1"

Main.scala:

import scala.language.implicitConversions
import scala.swing.{Action, _}

object Main extends App {

  implicit def pair2Dimension(pair: (Int, Int)): Dimension = new Dimension(pair._1, pair._2)

  val dim : Dimension = (0,0)

  println(dim)


}

Why does this happen? How is import scala.swing.{Action, _} hiding the implicitConversions import?

like image 489
Suma Avatar asked Jan 14 '15 11:01

Suma


3 Answers

As explained in

http://docs.scala-lang.org/tutorials/FAQ/finding-implicits.html

a wildcard import (as you wrote) gives to any Swing implicit definitions a quite high priority and that's clearly hiding yours.

Since you're compiling with SBT, wouldn't it be much easier to pass the following setting

scalacOptions ++= Seq(
  "-feature",
  "-language:implicitConversions"
)

and stop worrying about what's the right place to import scala.language.implicitConversions?

like image 68
pangiole Avatar answered Oct 23 '22 13:10

pangiole


How is import scala.swing.{Action, _} hiding the implicitConversions import?

Your:

import scala.language.implicitConversions

... gets shadowed by implicitConversions defined in scala.swing package object:

package scala

...

package object swing {

...

implicit lazy val implicitConversions = scala.language.implicitConversions

...

}

Since you are using wildcard import here:

import scala.swing.{Action, _}

... scala.swing.implicitConversions gets imported from scala.swing and in the end shadows scala.language.implicitConversions.

The interesting question is: why scalac fails to figure out that language feature is enabled if there are two "feature flags" (implicitConversions in this case), one shadowing another at the same level.

This might be a bug as well as specifics of how SIP 18 is implemented.

Anyway, to solve this I can suggest you to do one of the following:

  1. do not import scala.language.implicitConversions (as it is already imported when wildcard importing scala.swing)

  2. do not do wildcard import from scala.swing (do not pollute your scope and import what you need instead)

  3. do another import (that is not shadowed by another) of implicitConversions at your Main object level

like image 34
Eugene Loy Avatar answered Oct 23 '22 13:10

Eugene Loy


This is a bug in the implicit lookup.

Here is the same structure in more normal code, where the required implicit is the execution context.

(It doesn't matter whether the wildcard import is from a package object, or whether the other package is in the same compilation unit.)

Since the code compiles with an explicit global, it should compile with an implicit arg.

An implicit is available if it can be accessed without a prefix.

The binding precedence is not affected by source code order. Shadowing works in the usual way; a binding never shadows a binding of higher precedence.

/*
package object bound2 {
  implicit lazy val global = scala.concurrent.ExecutionContext.Implicits.global
}
*/
package bound2 {
  object B {
    implicit lazy val global: concurrent.ExecutionContextExecutor = scala.concurrent.ExecutionContext.global
  }
}

package bound {
  // the order of these imports in the same scope should not matter
  import scala.concurrent.ExecutionContext.Implicits.global
  import bound2.B._

  object Test extends App {
    val f = concurrent.Future(42)  //(global) // explicit arg works
    Console println concurrent.Await.result(f, concurrent.duration.Duration.Inf)
  }
}

In the spec example 2.0.1, adding the line marked "OK" compiles, and you can verify that order doesn't matter, but it becomes ambiguous in the inner scope because the "wildcard y" there does not shadow the "explicit y" from the outer scope:

    import X.y           // `y' bound by explicit import
    println("L16: "+y)   // `y' refers to `Q.X.y' here
    import P.X._
    println("OK: "+y)   // `y' refers to `Q.X.y' here

    locally { val x = "abc"      // `x' bound by local definition
      import P.X._       // `x' and `y' bound by wildcard import
//        println("L19: "+y) // reference to `y' is ambiguous here
like image 32
som-snytt Avatar answered Oct 23 '22 11:10

som-snytt