Is Lisp the only language with REPL?

前端 未结 7 1862
失恋的感觉
失恋的感觉 2020-12-13 06:59

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. H

相关标签:
7条回答
  • 2020-12-13 07:33

    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.

    • clj (clojure)
    • ghci (ghc)
    • ipython
    • irb (ruby)
    • js (spidermonkey)
    • node
    • python
    • sbcl
    • v8

    In fact implementing a trivial one in Ruby is fairly easy:

    repl = -> prompt { print prompt; puts(" => %s" % eval(gets.chomp!)) }
    loop { repl[">> "] }
    
    0 讨论(0)
  • 2020-12-13 07:36

    I think it is interesting to compare two approaches. A bare bones REPL loop in a Lisp system would look like this:

    (loop (print (eval (read))))
    

    Here are two actual Forth implementations of a REPL loop. I'm leaving nothing out here -- this is the full code to these loops.

    : DO-QUIT   ( -- )  ( R:  i*x -- )
        EMPTYR
        0 >IN CELL+ !   \ set SOURCE-ID to 0
        POSTPONE [
        BEGIN           \ The loop starts here
            REFILL      \ READ from standard input
        WHILE
            INTERPRET   \ EVALUATE  what was read
            STATE @ 0= IF ."  OK" THEN  \ PRINT
            CR
        REPEAT
    ;
    
    : quit
      sp0 @ 'tib !
      blk off
      [compile] [
      begin
        rp0 @ rp!
        status
        query           \ READ
        run             \ EVALUATE
        state @ not
        if ." ok" then  \ PRINT
      again             \ LOOP
    ;
    

    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:

    1. READ -- returns input from stdin
    2. EVAL -- process said input as an expression in the language
    3. PRINT -- print EVAL's result
    4. LOOP -- go back to READ

    Without going into implementation details, one can't distinguish a Lisp REPL from, for example, a Ruby REPL. As functions, they are the same.

    0 讨论(0)
  • 2020-12-13 07:38

    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 the READ 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:

    1. 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.

    2. 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:

    CL-USER 1 > (setf *products* '((shoe (100 euro))
                                   (shirt (20 euro))
                                   (cap (10 euro))))
    ((SHOE (100 EURO)) (SHIRT (20 EURO)) (CAP (10 EURO)))
    

    an order, a list of product and amount:

    CL-USER 2 > '((3 shoe) (4 cap))
    ((3 SHOE) (4 CAP))
    

    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.

    CL-USER 3 > (loop for (n product) in *
                      sum (* n (first (second (find product *products*
                                                    :key 'first)))))
    340
    

    But you can also compute Lisp code:

    Let's take a function which adds the squares of its two args:

    CL-USER 4 > '(defun foo (a b) (+ (* a a) (* b b))) 
    (DEFUN FOO (A B) (+ (* A A) (* B B)))
    

    The fourth element is just the arithmetic expression. * refers to the last value:

    CL-USER 5 > (fourth *)
    (+ (* A A) (* B B))
    

    Now we add some code around it to bind the variables a and b to some numbers. We are using the Lisp function LIST to create a new list.

    CL-USER 6 > (list 'let '((a 12) (b 10)) *)
    (LET ((A 12) (B 10)) (+ (* A A) (* B B)))
    

    Then we evaluate the above expression. Again, * refers to the last value.

    CL-USER 7 > (eval *)
    244
    

    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*?

    CL-USER 8 > *print-length*
    NIL
    

    Let's see how a list gets read and printed:

    CL-USER 9 > '(1 2 3 4 5)
    (1 2 3 4 5)
    

    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.

    CL-USER 10 > (set ++ 3)
    3
    

    Then above list prints differently. ** refers to the second previous result - data, not text.

    CL-USER 11 > **
    (1 2 3 ...)
    
    0 讨论(0)
  • 2020-12-13 07:40

    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.

    0 讨论(0)
  • 2020-12-13 07:41

    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

    Perl

    Python

    Ruby

    Scala

    Smalltalk -- I learned it on a REPL!

    EDIT I forgot about Java!

    0 讨论(0)
  • 2020-12-13 07:52

    How is Lisp REPL different from non-Lisp REPL?

    Let's compare Common Lisp's REPL with Python's IPython.

    The main two points are:

    • Lisp is an image-based language. There is no need to restart the process/the REPL/the whole app after a change. We compile our code function by function (with compiler warnings etc).
    • we don't loose state. Even more, when we update class definitions, our objects in the REPL are also updated, following rules we have control upon. That way we can hot-reload code in a running system.

    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.

    0 讨论(0)
提交回复
热议问题