2013-07-24 43 views
6

Làm thế nào để val ngầm định này gây ra một StackOverFlowError?scala ngầm gây ra StackOverflowError

(pared xuống mã ban đầu của tôi, vẫn còn gây ra lỗi)

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

Tôi có làm một cái gì đó bất hợp pháp với implicits?

Trả lời

9

Tôi tin rằng tôi có thể trả lời những gì đang diễn ra ở đây .. nó liên quan đến các chuyển đổi tiềm ẩn khác và chuyển đổi bạn vừa tạo. Nếu bạn thêm dấu vết này, bạn có thể xác nhận những gì tràn ngăn xếp thường liên quan đến - một chức năng tự xưng là lặp đi lặp lại cho đến khi không gian chồng treo java:

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

.... gọi nội 4,5 gọi nội 4,5 gọi nội 4,5 gọi nội 4,5 gọi 4.5ERROR nội: java.lang.StackOverflowError

tôi nghĩ rằng những gì đang xảy ra là thế này - toDouble không phải là một chức năng tự nhiên của chuỗi java, mà đúng hơn là xảy ra cách sử dụng một chuyển đổi ngầm trong StringOps (hoặc StringLike, Tôi không thực sự chắc chắn nhưng đó là cùng một vấn đề).

Vì vậy, khi bạn gọi đếnDouble - trình biên dịch bắt đầu tìm kiếm một chuyển đổi tiềm ẩn có thể chứa hàm "toDouble". Về lý thuyết, nó có thể là bất kỳ lớp kết quả nào.

NHƯNG - điều gì sẽ xảy ra nếu một số chuyển đổi tiềm ẩn có thể đạt được điều này? Rất tiếc, "Double" cũng chứa hàm toDouble như đã được chứng minh ở đây:

val x = 44.4 
x.toDouble 

Và hãy đoán xem? Điều đó có nghĩa là hàm ẩn mới của bạn, bây giờ gần nhất trong phạm vi thắng cuộc thi và nhận được gọi là trong một vòng tròn để thực hiện "toDouble" - có hiệu quả cố gắng để biến chuỗi thành một đôi, để gọi toDouble (trên lớp Double), nhiều lần. Tôi sẽ thừa nhận nó khá khó hiểu, nhưng bằng chứng phù hợp.

Đây là bản sửa lỗi .. nó phù hợp với lời giải thích và ngăn chặn các cuộc gọi đệ quy.

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

Có ai biết liệu điều này có đủ điều kiện để nhận lỗi không? Dường như với tôi phạm vi của một chuyển đổi tiềm ẩn, không nên hợp lệ trong mã xác định của chuyển đổi đó. (do đó, luôn bị loại trừ). Kết quả duy nhất có thể đến từ việc cho phép điều này, luôn luôn là một vòng lặp vô hạn – LaloInDublin

+0

Tôi nắm lấy cơ hội này có thể đáng để giải quyết và gửi vấn đề .. Ngôn ngữ lập trình Scala/SI-7693 – LaloInDublin

+0

Tôi nghĩ rằng đó là công bằng hơn khi nói Scala đã làm những gì nó đã được yêu cầu làm (tức là không phải là một lỗi). Tốt để gửi vấn đề và xem những gì sẽ xảy ra. Có thể có một cách thông minh để phát hiện và cảnh báo người dùng. Nhưng, vấn đề là có thể có bất kỳ số lượng và sự phức tạp của mã trước khi đệ quy. Ngoài ra, có lẽ ai đó muốn đệ quy đó và đã mã hóa một điều kiện thoát. – Core