Scala:导入如何防止找到隐式值?

Scala: How can an import prevent finding an implicit value?

我可以使用调试隐式的建议:

我想使用隐式,x:

type T
trait HasT {
  implicit def x: T = ...
}

但我还需要从某个包 foo 导入通配符。我尝试了两种不同的方式来介绍两者:

class UseT extends HasT {
  import foo._
  implicitly[T] // fails! "could not find implicit value"
  // use foo stuff
}

class UseT {
  object hasT extends HasT
  import hasT.x
  import foo._
  implicitly[T] // fails! "could not find implicit value"
}

两者都失败了 "could not find"(不是 "ambiguous implicits values")。

当隐式标识符 x: T 可通过继承或导入在方法调用点访问时,会发生这种情况。

我的解决方法是在导入之前将 x 重新绑定到隐式 val。以下两项工作:

implicit val x2: T = implicitly[T]
import foo._
implicitly[T] // works!

implicit val x2: T = x
import foo._
implicitly[T] // works!

foo 中可能有什么值会导致此行为?

我的第一个猜测是 foo 中存在一些竞争隐含,但如果它具有更高的优先级,则以下 implicitly 仍然有效,如果它是一个模棱两可的隐含,我' d 会得到不同的错误。

edit: Miles Sabin 的猜测是正确的!我发现阴影是隐含的:timeColumnType。我仍然不完全理解这是如何工作的,因为 Som Snytt 观察到隐藏隐式是通配符导入的(较低优先级),而隐藏是继承的(最高优先级的一部分),所以我将整个 post 留在这里为了 post 诚实。

收回miles sabin 提供的第二个猜测是implicit shadowing。我已经澄清了我的 post 以排除这种可能性。如果我尝试 package hasT extends HasT; import hasT._,这种情况将与我的错误一致,但正如 som-snytt 指出的那样,这两种情况不会导致阴影。

在我的具体情况下,这可以通过更改我尝试使用的隐式名称来确认。
(这是错误的。我在使用此测试进行验证时可能错过了 publishLocalreload。)

context:我实际上是在尝试使用 slick。上面隐含的T其实是一个列类型映射:

import slick.driver.JdbcProfile

class Custom { ... } // stored as `Long` in postgres

trait ColumnTypes {
  val profile: JdbcProfile
  import profile.api._ // this is `foo` above
  type T = profile.BaseColumnType[Custom]
  implicit def customColumnType: T = 
    MappedColumnType.base[Custom, Long](_.toLong, Custom.fromLong)
}

class DatabaseSchema(val profile: JdbcProfile) extends ColumnTypes {
  // `implicitly[T]` does not fail here.
  import profile.api._ // this is also `foo` above
  // `implicitly[T]` fails here, but it's needed for the following:
  class CustomTable(tag: Tag) extends Table[Custom](tag, "CUSTOMS") {
    // following fails unless I rebind customColumnType to a local implicit
    def custom = column[Custom]("CUSTOM")
    def * = custom
  }
}

api/foo的类型是JdbcProfile.API. The offending implicit is probably here,但我也说不出为什么。我会尝试阻止其中一些被导入,看看是否可以缩小范围。

我认为 foo 最有可能包含一个名为 x 的定义。当(通配符)从 foo 导入时,它会隐藏本地定义,

scala> object foo { val x: Boolean = true }
defined object foo

scala> implicit val x: Int = 23
x: Int = 23

scala> implicitly[Int]
res0: Int = 23

scala> import foo._
import foo._

scala> implicitly[Int]
<console>:17: error: could not find implicit value for parameter e: Int
       implicitly[Int]
                 ^

这显然是隐式搜索中的一个错误。

First, eligible are all identifiers x that can be accessed at the point of the method call without a prefix and that denote an implicit definition or an implicit parameter. An eligible identifier may thus be a local name, or a member of an enclosing template, or it may be have been made accessible without a prefix through an import clause.

示例中,无前缀的x指的是继承的符号。 X.x 没有前缀就无法访问。

隐式搜索正在摸索导入。

$ scala
Welcome to Scala 2.12.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_111).
Type in expressions for evaluation. Or try :help.

scala> :pa
// Entering paste mode (ctrl-D to finish)

object X { def x: Int = 42 }

trait T { def x: Int = 17 }

object Y extends T {
  import X._
  def f = x
}

// Exiting paste mode, now interpreting.

defined object X
defined trait T
defined object Y

scala> Y.f
res0: Int = 17

scala> :pa
// Entering paste mode (ctrl-D to finish)

object X { implicit def x: Int = 42 }

trait T { implicit def x: Int = 17 }

object Y extends T {
  import X._
  def f: Int = implicitly[Int]
}

// Exiting paste mode, now interpreting.

<pastie>:19: error: could not find implicit value for parameter e: Int
         def f: Int = implicitly[Int]
                                ^

scala> :pa
// Entering paste mode (ctrl-D to finish)

object X { implicit def x: Int = 42 }

trait T { implicit def x: Int = 17 }

object Y extends T {
  import X.{x => _, _}          // avoids bug, but is redundant
  def f: Int = implicitly[Int]
}

// Exiting paste mode, now interpreting.

defined object X
defined trait T
defined object Y

scala> 

另一个使用 REPL 的例子是这样限定的:

scala> :pa
// Entering paste mode (ctrl-D to finish)

object X { def x: Int = 42 }
object Y { implicit def x: Int = 17 }
object Z {
  import Y.x
  def f = {
    import X._
    x
  }
}

// Exiting paste mode, now interpreting.

<pastie>:19: error: reference to x is ambiguous;
it is imported twice in the same scope by
import X._
and import Y.x
           x
           ^

其中x根本不可用,正确排除了隐式。

确认一下:

$ scala -Xlog-implicits
Welcome to Scala 2.12.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_111).
Type in expressions for evaluation. Or try :help.

scala> :pa
// Entering paste mode (ctrl-D to finish)

object X { implicit def x: Int = 42 }

trait T { implicit def x: Int = 17 }

object Y extends T {
  import X._
  def f: Int = implicitly[Int]
}

// Exiting paste mode, now interpreting.

<console>:17: x is not a valid implicit value for Int because:
candidate implicit method x in object X is shadowed by method x in trait T
         def f: Int = implicitly[Int]
                                ^
<console>:17: x is not a valid implicit value for Int because:
candidate implicit method x in object X is shadowed by method x in trait T
         def f: Int = implicitly[Int]
                                ^
<console>:17: error: could not find implicit value for parameter e: Int
         def f: Int = implicitly[Int]
                                ^

scala> 

可能https://issues.scala-lang.org/browse/SI-9208