2013-07-24 16 views
6

W jaki sposób niejawny val powoduje błąd StackOverFlowError?niejawne przyczyny scala StackOverflowError

(pominięto w mój oryginalny kod, aby nadal spowodować błąd)

object Complicit { 
    // a class with name, default, and conversion function as implicit val 
    case class CC[A](name: String, defaultValue: A)(implicit val convert: String => A) { 
    def getFrom(s: String): A= try { 
     convert(s) 
    } catch { 
     case t: Throwable => 
     println("ERROR: %s".format(t)) // just to see the StackOverflowException 
     defaultValue 
    } 
    } 

    // this works fine 
    object Works { 
    val cc1= CC("first", 0.1)(_.toDouble) 
    } 

    // this causes java.lang.StackOverflowError due to the implicit 
    object Fails { 
    // !!! StackOverFlowError here 
    implicit val stringToDouble: String => Double= { _.toDouble } 

    val cc2= CC("second", 0.2) 
    } 

    def main(args: Array[String]) { 
    // this works 
    println("%s %f".format(Works.cc1.name, Works.cc1.getFrom("2.3"))) 
    // this fails 
    println("%s %f".format(Fails.cc2.name, Fails.cc2.getFrom("4.5"))) 
    } 
} 

robię coś nielegalnego z implicits?

Odpowiedz

9

Wierzę, że mogę odpowiedzieć na to, co się tutaj dzieje. Jest to związane z innymi niejawnymi konwersjami i tym, które właśnie utworzyłeś. Jeśli dodasz ten ślad można potwierdzić co przepełnienie stosu zwykle dotyczy - funkcja nazywająca siebie, aż przestrzeni stosu wypadków Java:

implicit val stringsToDouble: String => Double= { x=>println("called inner "+x); x.toDouble } 

.... nazwie wewnętrzna 4,5 zwana wewnętrzna 4,5 zwany wewnętrzny 4,5 zwana wewnętrzna 4,5 zwana wewnętrzna 4.5ERROR: java.lang.StackOverflowError

myślę, co się dzieje jest to - toDouble nie jest naturalną funkcją java ciąg, ale zdarza się przy użyciu niejawna konwersja w StringOps (lub StringLike, Nie jestem pewien, ale to jest ten sam problem).

Tak więc, gdy wywoływana jest opcjaDouble - kompilator rozpoczyna wyszukiwanie niejawnej konwersji, która może zawierać funkcję "toDouble". W teorii może to być dowolna powstała klasa.

ALE - co powinno się stać, jeśli kilka niejawnych konwersji może to osiągnąć? Niestety, "Double" zawiera także funkcjęDouble, co zostało tu udowodnione:

val x = 44.4 
x.toDouble 

I zgadnij co? Oznacza to, że twoja nowa funkcja niejawna, najbliższa teraz w zakresie, wygrywa konkurs i zostaje wywołana w kręgu, aby wykonać "toDouble" - skutecznie próbując zmienić ciąg znaków w podwójny, aby wywoływać do Double (w Double Double), wielokrotnie. Przyznaję, że to dość mylące, ale dowody się zgadzają.

Oto poprawka .. pasuje do wyjaśnienia i zapobiega wywołaniom cyklicznym.

implicit val stringsToDouble: String => Double= { java.lang.Double.parseDouble(_) } 
+0

Czy ktoś wie, czy kwalifikuje się jako błąd warty przesłania? Wydaje mi się, że zakres niejawnej konwersji nie powinien być prawidłowy w ramach kodu definiującego tę konwersję. (w związku z tym zawsze wyłączone). Jedynym rezultatem, który mógłby nadejść z tego powodu, byłaby zawsze nieskończona pętla. – LaloInDublin

+0

Zaryzykowałem to, być może warto było zająć się i przesłałem problem. Scala Programming Language/SI-7693 – LaloInDublin

+0

Myślę, że bardziej sprawiedliwie jest mówić Scala zrobił dokładnie to, o co go poproszono (tj. nie błąd). Dobrze jest zgłosić problem i zobaczyć, co się stanie. Może istnieje sprytny sposób na wykrycie i ostrzeżenie użytkownika. Ale problem polega na tym, że przed rekurencją może istnieć dowolna ilość i złożoność kodu. Być może ktoś chciał tej rekursji i zakodował warunki wyjścia. – Core

Powiązane problemy