2016-11-20 10 views
9

mogę używać sugestie debugowania niejawna:Scala: W jaki sposób import może uniemożliwić znalezienie ukrytej wartości?

chcę używać niejawny, x:

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

Ale muszę również import wieloznaczny od jakiegoś pakietu foo. Próbowałem dwa różne sposoby: wprowadzając zarówno

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" 
} 

Zarówno powiedzie się z „nie można znaleźć” (nie „wartości niejednoznaczne implicits”).

Dzieje się tak, gdy niejawny identyfikator x: T jest dostępny w punkcie wywołania metody przez dziedziczenie lub importowanie.

Moje obejście polega na ponownym powiązaniu x z niejawną wartością przed importem. Oba następujących prac:

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

i

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

Jaką wartość mogłaby być w foo spowoduje to zachowanie?

Moim pierwszym przypuszczeniem jest to, że niektóre konkurują domyślnie w foo, ale jeśli był wyższy priorytet, następująca implicitly nadal działałaby, a jeśli byłaby niejednoznaczna, otrzymywałbym inny inny błąd.

edycja: Przypuszczenie Milesa Sabina było poprawne! Znalazłem ukryte ukryte: timeColumnType. Wciąż nie do końca rozumiem, biorąc pod uwagę obserwację Som Snytta, że ​​ukryty niejawny był zaimportowany z użyciem symboli wieloznacznych, podczas gdy cień był dziedziczony. Zostawię tutaj cały post dla potomności:

Drugie przypuszczenie, oferowane przez mile sabin, to implicit shadowing. Od tego czasu wyjaśniłem swój post, aby wykluczyć taką możliwość. Ten przypadek byłby zgodny z moimi błędami, gdybym próbował package hasT extends HasT; import hasT._, ale jak zauważy som-snytt, te dwa przypadki nie spowodowałyby shadowingu.

W moim konkretnym przypadku można to potwierdzić, zmieniając nazwę niejawnego, którego próbuję użyć. (Musiałem tęsknić za publishLocal lub reload)

Teraz rzeczywiście próbuję użyć śliskich. Domniemana T wyżej faktycznie odwzorowanie typu kolumna

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 
    } 
} 

Typ api/foo jest JdbcProfile.API. Niewłaściwe naruszenie to prawdopodobnie here, ale nie mogę powiedzieć dlaczego. Spróbuję zablokować niektóre z nich przed importem i sprawdzić, czy mogę je zawęzić.

+0

Twój zmniejszona przykład kompiluje. Czy powinno? Zauważam, że nie pseudo "T" do "profile.api.BaseColu ...". Prawdopodobnie to też nie powinno mieć znaczenia. –

+0

@ som-snytt Right! Nie skompilowałoby się, gdyby niejawne miały nazwy 'timeColumnType'. Miles miał rację, podejrzewając ukryty cień. – stewSquared

+0

Zacząłem nazywać moje implicity pełną ścieżką pakietu w nazwie, aby uniknąć tego w przyszłości. – stewSquared

Odpowiedz

8

Myślę, że najprawdopodobniej foo zawiera definicję o nazwie x. Kiedy (wildcard) importowane z foo to cienie lokalną definicję,

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

Symbol wieloznaczny nigdy nie zacienia lokalnego lub odziedziczonego lub określonego importu. http://www.scala-lang.org/files/archive/spec/2.12/02-identifiers-names-and-scopes.html Tutaj 'x' jest niejednoznaczne, ponieważ jest' import $ line.x; {import foo._; x} 'tak niejawny nie jest odpowiedni. –

+0

To prawda, że ​​replikator wprowadza dodatkowy zakres dla późniejszego importu i niepowodzeniem "domyślnie", ale to nie znaczy, że shadowing nie odpowiada za niepowodzenie. –

+0

Gulasz mówi, że 'x' jest dostępny, więc' f (x) 'działa zamiast" x "jest niejednoznaczny, ponieważ zarówno zdefiniowany jak i zaimportowany. Właśnie tego próbowałem osiągnąć. Ale może "dostępny" oznaczał "x nie jest prywatny", czy nie nazwa jest poprawnie związana? –

2

Jest to oczywiście błąd w niejawny wyszukiwania.

pierwsze kwalifikują są identyfikatory x, które są dostępne w punkcie połączenia metodą bez prefiksu i oznaczają ukryte definicji lub domniemane parametru. Kwalifikujący się identyfikator może zatem być nazwą lokalną lub członkiem dołączającego szablonu lub może być udostępniony bez prefiksu przez klauzulę importu.

W tym przykładzie nieprefixowany x odnosi się do odziedziczonego symbolu. X.x jest niedostępny bez prefiksu.

Wyszukiwanie niejawne polega na wyszukiwaniu importu.

$ 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> 

Drugi przykład za pomocą rEPL jest zawężona w ten sposób:

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 
     ^

Gdzie x nie jest dostępna w ogóle, a niejawny jest poprawnie wyłączone.

Wystarczy potwierdzić:

$ 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> 

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

Powiązane problemy