关于复制驱动的编程

2021-01-04 18:41:41

很久以前,有人用“ entha_saava”句柄在Hacker News上发布了这个问题:

有知识的人可以解释一下Lisp REPL与Python / Ruby REPL有何不同吗? REPL驱动的开发的区别点是什么?

答案是,存在一种特殊的编程,您可以在运行时通过与之交互来构建程序,并且某些语言和运行时从头开始设计以支持这种编程。

为什么不?这就是entha_saava问题的症结所在,对吗?这些repl驱动的编程系统是什么,什么使它们与Python和Ruby以及其他提供arepl的语言不同?

repl这个词是缩写,代表read-eval-printloop。该术语来自Lisp的历史。从六十年代开始,使用Lisp的标准方法就是启动语言处理器,在其提示下键入表达式,然后等待它评估表达式并打印结果,然后再提示另一个表达式。阅读,评估,打印。循环。

如今,代表们风靡一时。每种语言及其兄弟提供了一个副本。有一个网站repl.it,其目的是提供所有代表。

当然,它实际上并没有提供所有代表。具有讽刺意味的是,它没有提供任何规范的repl驱动开发环境:Common Lisp和Smalltalk。

这使我们回到entha_saava的问题:如果Common Lisp和Smalltalk是repl驱动的环境,而Python和Ruby不是,那么有什么区别呢? Lisp和Smalltalk有什么Python和Ruby没有的?

他们拥有的是一种语言和运行时系统,它是从头开始设计的,假设您将要通过启动语言引擎并与之交谈,教它如何交互地成为您的程序,并在运行时对其进行更改来开发程序。

我已经听到反对意见的提出。我以前见过。是的,每种带有表示语言的语言都可以在其中执行某些操作。显然是这样;如果不是,那么repl将完全无用。

能够在repl中执行某些操作不会使引擎成为repl驱动的编程环境。老式的Lisp和Smalltalk环境与众不同的是,您可以在repl中进行所有操作。他们对您的工作没有任何不必要的限制;如果语言和运行时可以做到,那么repl可以做到。

例如,您可以通过在repl提示符下评估以下表达式,要求Clozure Common Lisp的当前版本从头开始重建自身:

关键不是您要一直这样一直重建CCL。关键是,REPL的功能没有人为限制。可以从代表那里获得开发系统的全部功能。

这是我在使用更新的,更少内容的回复时首先要注意的事情之一:我总是遇到无法从代表那里完成的事情。

不过,这不仅仅意味着不受限制。正确支持交互式编程意味着该语言及其运行时具有积极的功能,可以在程序运行时对其进行更改。

定义一个函数foo,该函数调用尚未定义的其他函数bar。现在调用foo。怎么了?

显然,对foo的调用会中断,因为未定义bar。但是当它破裂时会发生什么呢?接下来发生什么?

如果您最喜欢的代表是Python或Ruby或其他几十个现代代表,则答案很可能是打印出错误消息并返回其提示。在某些情况下,它可能会崩溃。

这个问题的答案是复制驱动程序设计的“差异点”。在老式的Lisp或Smalltalk环境中,foo中的中断会使您陷入中断循环。

中断循环是功能齐全的副本,具有主副本的所有工具,但它存在于中断功能的动态环境中。在breakloop中,您可以在挂起的调用堆栈上漫游,并检查每个堆栈帧中按词法可见的所有变量。实际上,您可以检查正在运行的程序中的所有实时数据。

此外,您可以编辑程序中的所有实时数据。如果您认为中断是由某个特定变量或字段中的错误值引起的,则可以以交互方式更改它并恢复已暂停的功能。如果现在可以正常工作,那就恭喜!你发现问题了!

而且,由于整个语言和开发系统都是可用的,不受限制,因此在repl中,您可以定义缺少的功能栏,恢复foo,并获得明智的结果。

实际上,这是Lisp和Smalltalk圈子中众所周知的一种编程风格,您可以在其中定义一个顶级函数,并调用尚未存在的其他函数,然后在产生的中断循环中定义这些函数。当您已经知道应该如何执行程序时,这是一种快速的方法。

如果您使用的是老式Lisp或Smalltalk系统,那么这听起来对您来说很明显,但是这种反应并不常见。惊喜更普遍,甚至令人怀疑:有什么收获?

要注意的是,您的语言系统的设计人员必须在规划阶段就考虑到该设施。事实发生后,再将其附加在驱动器上,就不会对它造成干扰。 Breakloop需要以交互的方式完全访问整个开发系统,并且将计算及其调用堆栈挂在breakloop的环境中。

让我们再举一个旨在支持交互式编程的工具示例。再一次,在您最喜欢的副本中尝试以下操作:

定义数据类型。我的意思是一个类,一个结构,一个记录类型-您喜欢的语言支持的任何用户定义的类型。做一些实例。编写一些函数(或方法,过程或任何其他方法)以对其进行操作。

您的语言运行时是否注意到类型的定义已更改?是否意识到现有实例具有新定义?当某个东西碰到其中一个时,它会自动重新初始化它以符合新定义吗?或者,如果它不知道该怎么做,它是否会启动一个中断循环并询问您如何处理?

如果答案是“是”,则说明您使用的是Lisp或Smalltalk系统。如果答案是“否”,那么您就缺少了REPL驱动的开发中的关键要素。

请记住:关键是要以交互方式支持编程。您不希望因为更改了定义而不得不终止程序并从头开始重建程序。真傻添加和更改定义是您的大部分工作!如果您的开发环境将支持交互式开发,则它最好知道在更改某些定义时如何保持程序运行。

老式的Lisp和Smalltalk系统知道该怎么做。还有一些其他类型的系统,大多数是较旧的系统,都知道该怎么做。

这些不是左领域之外的古怪新想法。他们已经存在了半个世纪。它们为交互式开发中的生产力做出了重大贡献。

现在,并不是每个程序员都喜欢这种开发。一些程序员更喜欢将开发视为设计,规划,制作蓝图以及在工作台上组装零件的过程。没什么错。实际上,已经建立了一个数十亿美元的国际产业。

但是,如果您更喜欢交互式开发,如果对您来说更自然,那么它可以使您的工作效率大大提高,更不用说工作中的快乐了。

具有适当的repl驱动环境的交互式开发是例外。大多数编程是通过其他方式完成的。

结果,那里有很多程序员甚至不曾听说过它,甚至不知道它的存在。 我的直觉是,这些程序员中有一部分会喜欢得到良好支持的交互式编程,并且如果他们知道这是什么,他们将从中受益。 也许如果有足够的程序员接触这种编程风格,那么我们将开始看到包含这种编程风格的新工具。