Lisp 是唯一具有 REPL 的语言吗?
除了 Lisp(ruby、scala)之外,还有其他语言声称它们使用 REPL(Read、Eval、Print、Loop),但尚不清楚 REPL 的含义是否与 Lisp 中的含义相同。 Lisp REPL 与非 Lisp REPL 有何不同?
There are languages other than Lisp (ruby, scala) that say they use REPL (Read, Eval, Print, Loop), but it is unclear whether what is meant by REPL is the same as in Lisp. How is Lisp REPL different from non-Lisp REPL?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
REPL 的想法来自 Lisp 社区。还有其他形式的文本交互界面,例如命令行界面。一些文本接口还允许执行某种编程语言的子集。
REPL 代表 READ EVAL PRINT LOOP:(loop (print (eval (read))))。
上述四个函数都是原始 Lisp 函数。
在 Lisp 中,REPL 不是命令行解释器 (CLI)。
READ
不读取命令,并且 REPL 不执行命令。READ
读取 s-表达式格式的输入数据并将其转换为内部数据。因此,READ 函数可以读取所有类型的 s 表达式——而不仅仅是 Lisp 代码。READ 读取 s-表达式。这是一种也支持编码源代码的数据格式。 READ 返回 Lisp 数据。
EVAL 以 Lisp 数据的形式获取 Lisp 源代码并对其进行评估。可能会发生副作用,并且 EVAL 返回一个或多个值。没有定义如何使用解释器或编译器来实现 EVAL。实现使用不同的策略。
PRINT 获取 Lisp 数据并将其作为 s 表达式打印到输出流。
LOOP 只是围绕此循环。在现实生活中,REPL 更为复杂,包括错误处理和子循环,即所谓的中断循环。如果出现错误,我们会在错误上下文中获取另一个 REPL,并添加了调试命令。一次迭代中产生的值也可以重新用作下一次评估的输入。
由于 Lisp 既使用代码即数据又使用函数元素,因此与其他编程语言略有不同。
相似的语言也会提供相似的交互界面。例如,Smalltalk 也允许交互式执行,但它不像 Lisp 那样使用 I/O 数据格式。对于任何 Ruby/Python/... 交互界面都是如此。
问题
那么,阅读表达式、评估它们并打印它们的值的最初想法有多重要?这与其他语言的功能相比是否重要:读取文本、解析文本、执行文本、选择性地打印某些内容以及选择性地打印返回值?通常返回值并没有被真正使用。
因此有两个可能的答案:
Lisp REPL 与大多数其他文本交互界面不同,因为它基于 s 表达式的数据 I/O 并对其求值的思想。< /p>
REPL 是描述编程语言实现或其子集的文本交互接口的通用术语。
Lisp 中的 REPL
在实际实现中,Lisp REPL 具有复杂的实现并提供大量服务,直至输入和输出对象的可点击表示(符号、CLIM、SLIME)。例如,高级 REPL 实现可以在 SLIME(一种流行的基于 Emacs 的 Common Lisp IDE)中找到,McCLIM,LispWorks 和 Allegro CL。
Lisp REPL 交互示例:
产品和价格列表:
订单、产品和金额列表:
订单的价格,
*
是一个变量,其中包含最后的 REPL 值。它不包含字符串形式的值,而是真正的实际数据。但您也可以计算 Lisp 代码:
让我们采用一个将两个参数的平方相加的函数:
第四个元素只是算术表达式。
*
指的是最后一个值:现在我们在它周围添加一些代码,将变量
a
和b
绑定到一些数字。我们使用 Lisp 函数LIST
来创建一个新列表。然后我们评估上面的表达式。同样,
*
指的是最后一个值。有几个变量会随着每次
REPL
交互而更新。先前值的示例为*
、**
和***
。还有+
用于先前的输入。这些变量的值不是字符串,而是数据对象。+
将包含 REPL 读取操作的最后结果。示例:变量
*print-length*
的值是多少?让我们看看如何读取和打印列表:
现在让我们将上面的符号
*print-length*
设置为 3。++
指的是读取的第二个输入,作为数据。SET
设置符号值。然后上面的列表打印不同。
**
指的是前第二个结果 - 数据,而不是文本。The idea of a REPL comes from the Lisp community. There are other forms of textual interactive interfaces, for example the command line interface. Some textual interfaces also allow a subset of some kind of programming language to be executed.
REPL stands for READ EVAL PRINT LOOP: (loop (print (eval (read)))).
Each of the four above functions are primitive Lisp functions.
In Lisp the REPL is not a command line interpreter (CLI).
READ
does not read commands and the REPL does not execute commands.READ
reads input data in s-expression format and converts it to internal data. Thus theREAD
function can read all kinds of s-expressions - not just Lisp code.READ reads a s-expression. This is a data-format that also supports encoding source code. READ returns Lisp data.
EVAL takes Lisp source code in the form of Lisp data and evaluates it. Side effects can happen and EVAL returns one or more values. How EVAL is implemented, with an interpreter or a compiler, is not defined. Implementations use different strategies.
PRINT takes Lisp data and prints it to the output stream as s-expressions.
LOOP just loops around this. In real-life a REPL is more complicated and includes error handling and sub-loops, so-called break loops. In case of an error one gets just another REPL, with added debug commands, in the context of the error. The value produced in one iteration also can be reused as input for the next evaluation.
Since Lisp is both using code-as-data and functional elements, there are slight differences to other programming languages.
Languages that are similar, those will provide also similar interactive interfaces. Smalltalk for example also allows interactive execution, but it does not use a data-format for I/O like Lisp does. Same for any Ruby/Python/... interactive interface.
Question:
So how significant is the original idea of READing EXPRESSIONS, EVALuating them and PRINTing their values? Is that important in relation to what other languages do: reading text, parsing it, executing it, optionally print something and optionally printing a return value? Often the return value is not really used.
So there are two possible answers:
a Lisp REPL is different to most other textual interactive interfaces, because it is based on the idea of data I/O of s-expressions and evaluating these.
a REPL is a general term describing textual interactive interfaces to programming language implementations or subsets of those.
REPLs in Lisp
In real implementations Lisp REPLs have a complex implementation and provide a lot of services, up to clickable presentations (Symbolics, CLIM, SLIME) of input and output objects. Advanced REPL implementations are for example available in SLIME (a popular Emacs-based IDE for Common Lisp), McCLIM, LispWorks and Allegro CL.
Example for a Lisp REPL interaction:
a list of products and prices:
an order, a list of product and amount:
The price for the order,
*
is a variable containing the last REPL value. It does not contain this value as a string, but the real actual data.But you can also compute Lisp code:
Let's take a function which adds the squares of its two args:
The fourth element is just the arithmetic expression.
*
refers to the last value:Now we add some code around it to bind the variables
a
andb
to some numbers. We are using the Lisp functionLIST
to create a new list.Then we evaluate the above expression. Again,
*
refers to the last value.There are several variables which are updated with each
REPL
interaction. Examples are*
,**
and***
for the previous values. There is also+
for the previous input. These variables have as values not strings, but data objects.+
will contain the last result of the read operation of the REPL. Example:What is the value of the variable
*print-length*
?Let's see how a list gets read and printed:
Now let's set the above symbol
*print-length*
to 3.++
refers to the second previous input read, as data.SET
sets a symbols value.Then above list prints differently.
**
refers to the second previous result - data, not text.REPL 的概念就是读取、评估、打印和读取。循环很多语言都有 REPL,这并不奇怪:
C/C++
C#/LINQ
Erlang
Haskell(在 Windows 上)
< a href="http://www.beanshell.org/" rel="nofollow noreferrer">Java
Javascript
Julia
Perl
Python
Ruby
Scala
Smalltalk -- 我在 REPL 上学到的!
Seeing as the concept of a REPL is to just Read, Eval, Print & Loop it's not too suprising that there are REPLs for many languages:
C/C++
C#/LINQ
Erlang
Haskell (on windows)
Java
Javascript
Julia
Perl
Python
Ruby
Scala
Smalltalk -- I learned it on a REPL!
我认为比较两种方法很有趣。 Lisp 系统中的基本 REPL 循环如下所示:
下面是 REPL 循环的两个实际的 Forth 实现。我不会在这里留下任何内容——这是这些循环的完整代码。
Lisp 和 Forth 做完全不同的事情,特别是在 EVAL 部分,但也在 PRINT 部分。然而,他们共享这样一个事实:两种语言的程序都是通过将其源代码提供给各自的循环来运行的,并且在这两种情况下代码只是数据(尽管在 Forth 情况下它更像是数据也是代码)。
我怀疑有人说只有 LISP 有 REPL 是 READ 循环读取 DATA,由 EVAL 解析,并创建一个程序,因为 CODE 也是 DATA。 Lisp 和其他语言之间的区别在很多方面都很有趣,但就 REPL 而言,这根本不重要。
让我们从外部考虑这一点:
不深入实现细节,人们无法区分例如,来自 Ruby REPL 的 Lisp REPL。作为功能,它们是相同的。
I think it is interesting to compare two approaches. A bare bones REPL loop in a Lisp system would look like this:
Here are two actual Forth implementations of a REPL loop. I'm leaving nothing out here -- this is the full code to these loops.
Lisp and Forth do completely different things, particularly in the EVAL part, but also in the PRINT part. Yet, they share the fact that a program in both languages is run by feeding its source code to their respective loops, and in both cases code is just data (though in Forth case it is more like data is also code).
I suspect what anyone saying only LISP has a REPL is that the READ loop reads DATA, which is parsed by EVAL, and a program is created because CODE is also DATA. This distinction is interesting in many respects about the difference between Lisp and other languages, but as far as REPL goes, it doesn't matter at all.
Let's consider this from the outside:
Without going into implementation details, one can't distinguish a Lisp REPL from, for example, a Ruby REPL. As functions, they are the same.
我猜你可能会说 Scala 的“REPL”是“RCRPL”:读取、编译、运行、打印。但由于编译器在内存中保持“热”状态,因此对于正在进行的交互来说速度相当快——启动只需要几秒钟。
I guess you could say that Scala's "REPL" is an "RCRPL": Read, Compile, Run, Print. But since the compiler is kept "hot" in memory, it's pretty fast for ongoing interactions--it just takes a few seconds to start up.
有很多人认为 REPL 的行为需要与 LISP 中的行为完全相同,否则它就不是真正的 REPL。相反,他们认为它是不同的东西,比如 CLI(命令行解释器)。老实说,我倾向于认为,如果它遵循以下基本流程:
,那么它就是 REPL。如前所述,有很多语言都具有上述功能。
有关此类讨论的示例,请参阅此 reddit 帖子。
There are a number of people that consider a REPL to needs to behave exactly like it does in LISP, or it's not a true REPL. Rather, they consider it something different, like a CLI (command line interpreter). Honestly, I tend to think that if it follows the basic flow of:
then it's a REPL. As noted, there are a lot of languages that have the above capability.
See this reddit thread for an example of such a discussion.
让我们将 Common Lisp 的 REPL 与 Python 的 IPython 进行比较。
主要两点是:
在 Python 中,通常,您启动 IPython 或者进入 ipdb。您定义一些数据,直到尝试新功能。您编辑源代码,并且想再试一次,因此退出 IPython 并重新开始整个过程。在 Lisp(主要是 Common Lisp)中,完全不是,它更具交互性。
Let's compare Common Lisp's REPL with Python's IPython.
The main two points are:
In Python, typically, you start IPython or you are dropped into ipdb. You define some data until you try out your new function. You edit your source, and you want to try again, so you quit IPython and you start the whole process again. In Lisp (Common Lisp mainly), not at all, it's all more interactive.
有一个名为
multi-repl
的不错的项目,它通过 Node.JS 公开各种 REPL:https://github.com/evilhackerdude/multi-repl
如果你查看支持的语言列表,很明显不仅 Lisp 具有 REPL 的概念。
事实上,在 Ruby 中实现一个简单的操作相当容易:
There's a nice project called
multi-repl
which exposes various REPLs via Node.JS:https://github.com/evilhackerdude/multi-repl
If you look at the list of supported languages, it's quite clear that not only Lisp has the concept of a REPL.
In fact implementing a trivial one in Ruby is fairly easy: