[英]Recursive overloading semantics in the Scala REPL - JVM languages
使用Scala的命令行REPL:
def foo(x: Int): Unit = {}
def foo(x: String): Unit = {println(foo(2))}
給
error: type mismatch;
found: Int(2)
required: String
看來你無法在REPL中定義重載的遞歸方法。 我認為這是Scala REPL中的一個錯誤並提交了它,但它幾乎立即關閉了“wontfix:我沒有看到任何方式這可以支持解釋器的語義,因為這兩個方法必須編譯一起。” 他建議將方法放在一個封閉的對象中。
是否有JVM語言實現或Scala專家可以解釋原因? 我可以看到,如果這些方法相互調用,那將是一個問題,但在這種情況下?
或者,如果這個問題太大而且您認為我需要更多必備知識,那么是否有人有關於語言實現的書籍或網站的任何良好鏈接,特別是在JVM上? (我知道約翰羅斯的博客,以及編程語言語用學一書......但這就是它。:)
問題是由於解釋器通常必須用給定名稱替換現有元素,而不是重載它們。 例如,我經常會嘗試一些東西,經常創建一個名為test
的方法:
def test(x: Int) = x + x
稍后,讓我們說我正在運行一個不同的實驗,我創建另一個名為test
方法,與第一個無關:
def test(ls: List[Int]) = (0 /: ls) { _ + _ }
這不是一個完全不切實際的場景。 事實上,正是大多數人使用解釋器的方式,通常都沒有意識到。 如果解釋器任意決定將兩個版本的test
都保留在范圍內,那么可能會導致使用測試時出現混淆的語義差異。 例如,我們可能會調用test
,意外地傳遞Int
而不是List[Int]
(不是世界上最不可能發生的事故):
test(1 :: Nil) // => 1
test(2) // => 4 (expecting 2)
隨着時間的推移,解釋器的根范圍將變得非常混亂各種版本的方法,字段等。我傾向於讓我的解釋器一次打開幾天,但如果允許這樣的超載,我們將被迫“因為事情變得太混亂,所以經常翻譯“翻譯”。
它不是JVM或Scala編譯器的限制,而是一個深思熟慮的設計決策。 正如bug中所提到的,如果你在root范圍之外的其他東西,你仍然可以重載。 將測試方法包含在一個類中似乎是對我來說最好的解決方案。
% scala28
Welcome to Scala version 2.8.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_20).
Type in expressions to have them evaluated.
Type :help for more information.
scala> def foo(x: Int): Unit = () ; def foo(x: String): Unit = { println(foo(2)) }
foo: (x: String)Unit <and> (x: Int)Unit
foo: (x: String)Unit <and> (x: Int)Unit
scala> foo(5)
scala> foo("abc")
()
如果您復制兩行並同時粘貼,則REPL將接受。
如臨時演員的答案所示,可能會超載。 丹尼爾關於設計決策的評論是正確的,但我認為不完整且有點誤導。 沒有超載的禁止 (因為它們是可能的),但它們並不容易實現。
導致這種情況的設計決策是:
問題是......如何實現所有這些目標? 我們如何處理您的示例?
def foo(x: Int): Unit = {}
def foo(x: String): Unit = {println(foo(2))}
從第4項開始,只能在class
, trait
, object
或package object
定義val
或def
。 所以,REPL將定義放在對象中,就像這樣( 不是實際的表示! )
package $line1 { // input line
object $read { // what was read
object $iw { // definitions
def foo(x: Int): Unit = {}
}
// val res1 would be here somewhere if this was an expression
}
}
現在,由於JVM的工作原理,一旦定義了其中一個,就無法擴展它們。 當然,您可以重新編譯所有內容,但我們放棄了。 所以你需要把它放在一個不同的地方:
package $line1 { // input line
object $read { // what was read
object $iw { // definitions
def foo(x: String): Unit = { println(foo(2)) }
}
}
}
這就解釋了為什么你的例子不是重載:它們在兩個不同的地方定義。 如果將它們放在同一行中,它們將被一起定義,這將使它們重載,如示例中所示。
至於其他設計決策,每個新包導入定義和以前包中的“res”,並且導入可以相互影響,這使得“重新定義”東西成為可能。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.