Scala REPL中的递归重载语义-JVM语言

时间:2020-03-06 14:35:44  来源:igfitidea点击:

使用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上? (我了解John Rose的博客以及《 Programming Language Pragmatics ...》一书,仅此而已。:)

解决方案

如果我们同时复制和粘贴这两行,则REPL会接受。

问题是由于这样的事实,解释器通常必须用给定的名称替换现有元素,而不是重载它们。例如,我经常会进行一些实验,经常创建一个名为" test"的方法:

def test(x: Int) = x + x

再过一会儿,假设我正在运行一个不同的实验,并且创建了另一个名为" test"的方法,该方法与第一个方法无关:

def test(ls: List[Int]) = (0 /: ls) { _ + _ }

这不是一个完全不现实的情况。实际上,这正是大多数人使用解释器的方式,常常甚至没有意识到。如果解释器任意决定将两个版本的test都保留在范围内,则可能导致在使用test时造成混淆的语义差异。例如,我们可能会调用" test",而意外地传递了一个" Int"而不是" List [Int]"(这不是世界上最不可能的事故):

test(1 :: Nil)  // => 1
test(2)         // => 4  (expecting 2)

随着时间的推移,解释器的根范围会得到令人难以置信的各种版本的方式,领域等,我倾向于离开我的翻译开了几天的时间,但如果超载这样的允许,我们将被迫"混乱冲洗"的解释,每隔一段时间为东西得过于混乱。

这不是JVM或者Scala编译器的限制,而是有意的设计决策。如该错误中所述,如果我们不在根作用域之内,则仍然可以重载。对我来说,将测试方法包含在类中似乎是最好的解决方案。

% 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")
()