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"
}
Оба терпят неудачу с "не мог найти" (не "неоднозначные значения значений").
Это происходит при неявном идентификаторе 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
все равно будет работать, и если бы это было двусмысленным неявным, я бы получил другую ошибку.
редактировать: Майлз Сабин был прав! Я обнаружил, что затенение неявное: timeColumnType. Я до сих пор не до конца понимаю, как это работает, учитывая замечание Сом Снитта о том, что неявное затенение импортировалось с помощью подстановочного знака (более низкий приоритет), а затененное наследовалось (часть с наивысшим приоритетом), поэтому я оставлю весь пост здесь для потомков.
Отвлечено: Второе предположение, предложенное Мили Сабином, - это скрытое затенение. С тех пор я уточнил свой пост, чтобы исключить эту возможность. Этот случай будет соответствовать моим ошибкам, если бы я попытался package hasT extends HasT; import hasT._
, но, как указывает сом-снитт, эти два случая не приведут к затенению.
В моем конкретном случае это можно подтвердить, изменив имя скрытого, которое я пытаюсь использовать.
(Это неверно. Я, вероятно, пропустил publishLocal
или же reload
используя этот тест для проверки.)
контекст: я на самом деле пытаюсь использовать пятно. Неявный 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
, Обидное неявное, вероятно, здесь, но я не могу сказать, почему. Я попытаюсь заблокировать некоторые из них от импорта и посмотрим, смогу ли я сузить это.
2 ответа
Я думаю, что наиболее вероятно, что 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]
^
Это явно ошибка в неявном поиске.
Во-первых, допустимы все идентификаторы x, к которым можно получить доступ в точке вызова метода без префикса и которые обозначают неявное определение или неявный параметр. Таким образом, допустимый идентификатор может быть локальным именем или членом включающего шаблона, или он может быть сделан доступным без префикса через условие импорта.
В примере без префикса 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>