2016-11-20 8 views
9

я мог бы использовать предложения отладки неявный: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 Use T { 
    object hasT extends HasT 
    import hasT.x 
    import foo._ 
    implicitly[T] // fails! "could not find implicit value" 
} 

И потерпеть неудачу с «не может найти» (не «неоднозначные implicits значения»).

Это происходит, когда неявный идентификатор 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 есть некоторая конкурирующая неявная, но если бы это был более высокий приоритет, то все еще работало бы следующее implicitly, и если бы это было неоднозначное неявное, у меня возникла бы другая ошибка.

Редактировать: Miles Sabin's guess was correct! Я нашел скрытый неявный: timeColumnType. Я до сих пор не совсем понимаю, учитывая мнение Сома Снытта о том, что скрытое неявное было подстановочным знаком, в то время как затененный был унаследован. Но я оставлю весь пост здесь для потомков:

Второе предположение, предложенное милями сабином, - implicit shadowing. С тех пор я разъяснил свой пост, чтобы исключить эту возможность. Этот случай был бы совместим с моими ошибками, если бы я пробовал package hasT extends HasT; import hasT._, но, как указывает som-snytt, эти два случая не приведут к затенению.

В моем конкретном случае это можно подтвердить, изменив имя неявного, которое я пытаюсь использовать. (Возможно, я пропустил 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. Оскорбительный неявный, вероятно, here, но я не могу сказать почему. Я попытаюсь заблокировать некоторые из них от импорта и посмотреть, могу ли я сузить его.

+0

Ваш уменьшенный пример компилируется. Предполагается ли это? Я заметил, что вы не псевдоним 'T'' profile.api.BaseColu ... '. Наверное, это тоже не имеет значения. –

+0

@ som-snytt Право! Он не будет компилироваться, если имплицитный был назван 'timeColumnType'. Майлз был прав, чтобы подозревать скрытое затенение. – stewSquared

+0

Я начал называть мои implicits полным пути пакета по имени, чтобы избежать этого в будущем. – stewSquared

ответ

8

Я думаю, что наиболее вероятно, что 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] 
       ^
+1

Подстановочный знак никогда не тени локального или унаследованного или конкретного импорта. http://www.scala-lang.org/files/archive/spec/2.12/02-identifiers-names-and-scopes.html Здесь 'x' неоднозначно, потому что это' import $ line.x; {import foo._; x} ', поэтому неявный не имеет права. –

+0

Верно, что repl вводит дополнительную область для последующего импорта и «неявно», но это не означает, что задержка не отвечает за отказ. –

+0

Stew говорит, что 'x' доступен, поэтому' f (x) 'работает вместо того, чтобы получать' x является двусмысленным, потому что оба определены и импортированы'. Это то, к чему я пытался добраться. Но, возможно, «доступный» означает «х не является частным», а не то, что имя правильно связано? –

2

Это явно ошибка в неявном поиске.

Во-первых, право все идентификаторы х, которые могут быть доступны в точке вызова метода без префикса и которые обозначают неявное определение или неявный параметр. Соответствующий идентификатор может, таким образом, быть локальным именем или членом закрывающего шаблона, или может быть , были доступны без префикса через предложение импорта.

В этом примере unsrefixed 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