Clojure REPL和Scala REPL有什么区别?

9
我已经使用Scala语言工作了几个月,已经在Scala中创建了几个项目。我发现Scala REPL(至少是它的IntelliJ工作表实现)非常适合快速开发。我可以编写代码,看到它的执行结果,感觉很好。但是,我只能对函数进行此操作(而不是整个程序)。我无法启动我的应用程序并立即更改它。或者至少我不知道如何做(如果您知道,请给我一些建议)。
几天前,我的同事告诉我关于Clojure REPL。他使用Emacs进行开发,并且可以即时更改代码并查看结果,无需重新启动。例如,他启动进程,如果更改函数的实现,他的代码将更改行为而无需重新启动。我希望在Scala语言中也能有同样的功能。
P.S. 我不想讨论哪种语言更好,也不想讨论函数式编程是否比面向对象编程更好。我想找到一个好的解决方案。如果Clojure是更好的任务语言,那就让它成为吧。

2
一个用于编写Clojure,另一个用于编写Scala。此外:https://dev59.com/hXE95IYBdhLWcg3wEpvo - vptheron
可能这对我来说有点难以知道一个正确答案会是什么样的?你也许在问Scala REPL是否可以在运行中重新定义函数吗? - Arthur Ulfeldt
好奇心很好,但标题不太合适。 - Mars
1个回答

22
简短回答是Clojure的设计采用了一个非常简单的单遍编译器,它一次读取和编译一个s表达式或形式。好或坏的是,没有全局类型信息、类型推断和全局分析或优化。Clojure使用clojure.lang.Var实例通过一系列从文本符号到事务性值的哈希映射创建全局绑定。def形式在这个全局绑定映射中都创建全局作用域的绑定。所以,在Scala中,“函数”(方法)将被解析为给定JVM类上的实例或静态方法,而在Clojure中,“函数”(def)实际上只是对变量绑定表格中条目的引用。当调用函数时,没有静态链接到另一个类,相反,变量是通过符号名称引用的,然后取消引用以获取一个clojure.lang.IFn对象的实例,然后调用它。
这种间接层意味着可以一次只重新评估一个定义,并且该重新评估对重新定义的var的所有客户端全局可见。
相比之下,当Scala中的定义发生变化时,scalac必须重新加载更改后的文件、宏展开、类型推断、类型检查和编译。然后由于JVM上的类加载语义,scalac还必须重新加载所有依赖于已更改的类中的方法的类。同时所有值,这些值是已更改的类的实例,都变得无用。

这两种方法各有优点和缺点。显然,Clojure的方法更容易实现,但由于不断的函数查找操作,会产生性能方面的持续代价并且忽略了正确性问题,因为缺少静态类型等等。这可以说适用于在短时间内发生许多变化的情境(交互式开发),但不适用于代码大多静态的情境(部署,因此是 Ox Cart)。我做的一些工作表明,由于缺少静态方法链接,Clojure程序的减速程度在16-25%左右。这并不是要称Clojure慢或Scala快,它们只是有不同的优先级。

Scala选择在编译应用程序时做更多的工作,从而使其性能更好,这可能更适合应用程序部署,当很少或没有重新加载时,但当您想进行许多小更改时,会产生阻力。

我手头有关于编译Clojure代码的一些材料,基本上按发布顺序排列,因为Nicholas对我的GSoC工作影响很大。

我想这让我只能不幸地说:“对不起,Scala在代码热交换方面的设计不像Clojure那样。”


2
Gilad Bracha和其他Smalltalk和Lisp世界的杰出人物有一个口号:“静态类型是耦合”,这是另一个例子。目前我们所知,实现静态类型语言的活跃环境是不可能的,而动态类型语言具有非常强大的活跃环境,可以查看MIT Lisp Machine、Squeak/Pharo Smalltalk、Newspeak、Dan Ingall's Lively Kernel或Self World。 - Jörg W Mittag
非常感谢大家! - Curiosity
1
@JörgWMittag 在动态性和静态可检查性之间总是存在权衡。有时你想要一个,有时你想要另一个。我倾向于静态类型方面 - 主要是因为我看到了很多在Java中发生的错误,因为它的类型系统太宽松了。这并不是要否认动态类型的优点,对我来说,静态类型的优点更加重要。你的情况可能会有所不同。 - Cubic
感谢您提供这篇信息丰富的文章。您提供的有关Clojure编译的链接无法使用 - 是否可以更新它们?谢谢。 - Zuriar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接