-->

递归超载语义在斯卡拉REPL - JVM语言(Recursive overloading sem

2019-09-21 11:14发布

使用Scala的命令行REPL:

def foo(x: Int): Unit = {}
def foo(x: String): Unit = {println(foo(2))}

error: type mismatch;
found: Int(2)
required: String

看来你不能定义在REPL重载递归方法。 我认为这是在斯卡拉REPL一个错误,提起它,但它几乎立即与“wontfix关闭:我不认为这可能支持给予解释的语义的方式,因为这两种方法都必须被编译一起。” 他建议把方法在一个封闭的对象。

是否有一个JVM语言实现或斯卡拉专家谁可以解释,为什么? 我可以看到这将是一个问题,如果方法叫对方实例,但在这种情况下?

或者,如果这是太大的问题,你觉得我需要更多的必备知识,是否有人有任何好的链接到有关语言实现的书籍或网站,尤其是在JVM上? (我知道约翰·罗斯的博客,这本书编程语言语......但仅此而已。:)

Answer 1:

这个问题是由于这样的事实,解释在多数情况下与一个给定的名称,以取代现有的元素,而不是重载它们。 例如,我经常会通过与实验的东西,往往造成一种称为运行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或斯卡拉编译器的限制,这是一个经过深思熟虑的设计决定。 正如错误所提到的,你仍然可以,如果你不是根范围以外的东西内超载。 一类内封闭测试方法似乎是对我最好的解决方案。



Answer 2:

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


Answer 3:

如果复制两行REPL将接受并在同一时间都粘贴。



Answer 4:

如图所示通过即兴的回答,有可能超载。 丹尼尔的有关设计决定的评论是正确的,但是,我认为,不完整的,有点误导。 有没有超载的取缔 (因为它们是可能的),但它们不容易实现的。

设计决策导致这种是:

  1. 所有以前的定义必须是可用的。
  2. 只有新输入的代码被编译,而不是重新编译的一切,从来没有入过每一次。
  3. 它必须能够重新定义(如丹尼尔提到)。
  4. 它必须能够以限定构件如瓦尔斯和DEFS,不仅类和对象。

问题是...如何实现这些目标? 我们如何处理您的例子吗?

def foo(x: Int): Unit = {}
def foo(x: String): Unit = {println(foo(2))}

与第四项开始,A valdef只能在内部定义的classtraitobjectpackage object 。 所以,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)) }
    }
  }
}

这解释了为什么你的例子并不重载:它们是在两个不同的地方定义。 如果你把它们放在同一行,他们全被定义在一起,这将使他们的过载,如图即兴的例子。

至于其他的设计决策,每一个新的包导入定义,并从以前的包“资源”,而进口可能影对方,这使得它能够“重新定义”的东西。



文章来源: Recursive overloading semantics in the Scala REPL - JVM languages