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> 

Вероятно, https://issues.scala-lang.org/browse/SI-9208

Другие вопросы по тегам