2013-06-24 16 views
5

Poniżej przedstawiono uproszczoną wersję moim prawdziwy problem:Scala bug w utajonego parametru

class Z[T] 
object E extends Enumeration { 
    implicit val z = new Z[Value] 
    val X, Y = Value 
} 
implicit def f[T : Z] = (getter: T) => 0 
implicit def o[T](v: Option[T])(implicit toInt: T => Int) = 0 
def e: Option[E.Value] = null 
val b: Int = e 

działa, z B pośrednio przekształca się o (e) (f (E.z)). Ale z małymi zmianami następujący:

implicit def f[T : Z] = (setter: T => Unit) => 0 
implicit def o[T](v: Option[T])(implicit toInt: (T => Unit) => Int) = 0 

zawiedzie znalezienie odpowiedniej wartości E.z niejawny chociaż nie ma istotnej różnicy od oryginalnego kodu, podczas gdy instrukcja jednoznaczne przekształcenie O (e) (f (E.z)) nadal działa.

Wiem, że implementacja niejawnego parametru nie jest jeszcze ukończona i nadal istnieje wiele nierozwiązanych problemów. Jeśli jest to jeden z nich, chciałbym zgłosić to do współpracowników Scala. Moje pytanie brzmi: a) czy to naprawdę błąd? b) jeśli tak, to gdzie i jak mogę zgłosić błąd, aby mógł zostać naprawiony w przyszłości?

UPDATE odpowiedź

Travisa pracował jak urok! Nawiasem mówiąc, powyższy kod było obejście do mojego pierwotnego problemu:

implicit object E extends Enumeration { val X, Y = Value } 
implicit object F extends Enumeration { val X, Y = Value } 
implicit def f[T <: Enumeration](getter: T#Value)(implicit e: T) = 0 
implicit def o[T](v: Option[T])(implicit toInt: T => Int) = 0 
val b: Int = Some[E.Value](null) 

W tym kodzie, sytuacja była na odwrót: to działa z wersją setter ale nie z prostszej wersji pochłaniacza. Kompilator narzeka, że ​​mylące jest używanie E lub F jako niejawnego parametru, chociaż użycie F nie kompiluje się ani nie ma sensu. Udało mi się dostać pracy robiąc coś podobnego:

implicit def f[S <% T => T, T <: Enumeration](getter: T#Value)(implicit e: T) = 0 

To działa, i chociaż jakoś mogła dostać pracy, ja nadal nie rozumiem logikę tej magii.

+0

Interesujące. Działając z '-Xlog-implicits', wydaje się, że' T' w 'f [T: Z]' nie jest wnioskowane jako 'E.Value'. Stąd działa to: 'val b: Int = o (e) (f [E.Value])' ale to nie: 'val b: Int = o (e) (f)'. – gourlaysama

Odpowiedz

6

Znalazłeś kolejną wersję this limitation systemu inferencyjnego typu Scala.

Kompilator rozwiąże T dla f jeśli w pierwszym przypadku, w którym to szuka niejawna konwersja z czystym starych E.Value do Int, ale nie w drugiej, gdzie chce konwersję z E.Value => Unit (tj Function1[E.Value, Unit]) do Int.

Na szczęście istnieje prosty obejście w takich przypadkach, po prostu użyć widoku zobowiązana:

implicit def f[F <% T => Unit, T: Z] = (setter: F) => 0 

Będzie desugar coś jak poniżej:

implicit def f[F, T](implicit st: F <:< (T => Unit), ev: Z[T]) = (setter: F) => 0 

Teraz gdy kompilator chce konwersji od E.Value => Unit do Int będzie w stanie natychmiast rozwiązać F na E.Value => Unit, a następnie T na E.Value.

+0

Dzięki za odpowiedź, działało jak czar! Ale nadal nie rozumiem, jak działa ta magia. Czy możesz rzucić okiem na powyższą aktualizację i podać wyjaśnienie? Wygląda na to, że S nie musi nawet mieć sensu, żeby system działający jako typ analizy działał ... Tajemniczy. –