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 指出的那样,这两种情况不会导致阴影。
在我的具体情况下,这可以通过更改我尝试使用的隐式名称来确认。
(这是错误的。我在使用此测试进行验证时可能错过了 publishLocal
或 reload
。)
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>
我可以使用调试隐式的建议:
我想使用隐式,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 指出的那样,这两种情况不会导致阴影。
在我的具体情况下,这可以通过更改我尝试使用的隐式名称来确认。
(这是错误的。我在使用此测试进行验证时可能错过了 publishLocal
或 reload
。)
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>