Является ли Лисп единственным языком с REPL? - PullRequest
18 голосов
/ 15 апреля 2011

Существуют языки, отличные от Lisp (ruby, scala), которые говорят, что они используют REPL (Read, Eval, Print, Loop), но неясно, является ли то, что подразумевается под REPL, таким же, как в Lisp. Чем Lisp REPL отличается от non-Lisp REPL?

Ответы [ 7 ]

32 голосов
/ 15 апреля 2011

Идея 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.То же самое для любого интерактивного интерфейса Ruby / Python / ....

Вопрос:

Итак, насколько важна первоначальная идея ЧТЕНИЯ ВЫРАЖЕНИЙ, ОЦЕНКИ их и ПЕЧАТИ их значений?Это важно по отношению к тому, что делают другие языки: чтение текста, его синтаксический анализ, выполнение, при желании что-то напечатать и, возможно, печать возвращаемого значения?Часто возвращаемое значение на самом деле не используется.

Таким образом, есть два возможных ответа :

  1. Lisp REPL отличается от большинства других текстовых интерактивных интерфейсов, потому что он основан на идееввода-вывода данных s-выражений и их оценки.

  2. a REPL - это общий термин, описывающий текстовые интерактивные интерфейсы для реализаций языка программирования или их подмножеств.

REPL в Lisp

В реальных реализациях Lisp REPL имеют сложную реализацию и предоставляют множество услуг, вплоть до интерактивных презентаций (Symbolics,CLIM, SLIME) объектов ввода и вывода.Расширенные реализации REPL доступны, например, в SLIME (популярной интегрированной среде разработки для Emacs для Common Lisp), McCLIM, LispWorks и Allegro CL .

Пример взаимодействия Lisp REPL :

список продуктов и цен:

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

заказ, список продуктов и количество:

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

Цена заказа, * - это переменная, содержащая последнее значение REPL.Оно не содержит это значение в виде строки, но содержит реальные фактические данные.

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

Но вы также можете вычислить код на Лиспе:

Давайте возьмем функцию, которая добавляет квадраты своих двухargs:

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

Четвертый элемент - это просто арифметическое выражение.* относится к последнему значению:

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

Теперь мы добавим вокруг него код, чтобы связать переменные a и b с некоторыми числами.Мы используем функцию Lisp LIST для создания нового списка.

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

Затем мы оцениваем вышеприведенное выражение. Опять же, * относится к последнему значению.

CL-USER 7 > (eval *)
244

Существует несколько переменных, которые обновляются при каждом взаимодействии REPL. Примерами являются *, ** и *** для предыдущих значений. Также есть + для предыдущего ввода. Эти переменные имеют в качестве значений не строки, а объекты данных. + будет содержать последний результат операции чтения REPL. Пример:

Каково значение переменной *print-length*?

CL-USER 8 > *print-length*
NIL

Посмотрим, как список читается и печатается:

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

Теперь давайте установим вышеуказанный символ *print-length* в 3. ++ относится ко второму предыдущему прочитанному вводу, как к данным. SET устанавливает значение символа.

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

Тогда вышеприведенный список печатается по-другому. ** относится ко второму предыдущему результату - данные, а не текст.

CL-USER 11 > **
(1 2 3 ...)
12 голосов
/ 15 апреля 2011

Поскольку концепция REPL заключается в простом чтении, оценке, печати и цикле, неудивительно, что существуют REPL для многих языков:

C / C ++

C # / LINQ

Erlang

Haskell (на окнах)

Java

Javascript

Perl

Python

Рубин

Scala

Smalltalk - я узнал об этом на REPL!

РЕДАКТИРОВАТЬ Я забыл о Java!

5 голосов
/ 15 апреля 2011

Я думаю, что интересно сравнить два подхода. Простой цикл REPL в системе Lisp будет выглядеть так:

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

Вот две фактические реализации Forth цикла REPL. Я ничего не оставляю здесь - это полный код этих циклов.

: 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 и Forth делают совершенно разные вещи, особенно в части EVAL, но также и в части PRINT. Тем не менее, они разделяют тот факт, что программа на обоих языках запускается путем подачи своего исходного кода в соответствующие циклы, и в обоих случаях код - это просто данные (хотя в случае Forth это больше похоже на данные, также код).

Я подозреваю, что любой, кто говорит, что только LISP имеет REPL, состоит в том, что цикл READ читает DATA, который анализируется EVAL, и создается программа, потому что CODE также является DATA. Это различие интересно во многих отношениях в отношении различий между Лиспом и другими языками, но для REPL это не имеет значения.

Давайте рассмотрим это со стороны:

  1. READ - возвращает ввод из стандартного ввода
  2. EVAL - обрабатывать указанный ввод как выражение на языке
  3. PRINT - распечатать результат EVAL
  4. LOOP - вернуться к READ

Не вдаваясь в детали реализации, нельзя отличить REPL Lisp от, например, Ruby REPL. Как функции, они одинаковы.

4 голосов
/ 15 апреля 2011

Полагаю, вы могли бы сказать, что Scala "REPL" - это "RCRPL": чтение, компиляция, запуск, печать.Но так как компилятор остается «горячим» в памяти, он довольно быстр для текущих взаимодействий - для запуска требуется всего несколько секунд.

3 голосов
/ 15 апреля 2011

Есть ряд людей, которые считают, что REPL должен вести себя точно так же, как в LISP, или это не настоящий REPL.Скорее, они считают это чем-то другим, например CLI (интерпретатором командной строки).Честно говоря, я склонен думать, что если он следует базовому потоку:

  • чтение ввода от пользователя
  • оценка этого ввода
  • печать вывода
  • вернитесь назад к чтению

, тогда это REPL.Как уже отмечалось, есть много языков, которые имеют вышеуказанную возможность.

См. эту ветку reddit для примера такого обсуждения.

2 голосов
/ 15 апреля 2011

Есть хороший проект под названием multi-repl, который предоставляет различные REPL через Node.JS:

https://github.com/evilhackerdude/multi-repl

Если вы посмотрите на список поддерживаемых языков, то совершенно очевидно, что нетолько Лисп имеет концепцию REPL.

  • clj (clojure)
  • ghci (ghc)
  • ipython
  • irb (ruby)
  • js (spidermonkey)
  • узел
  • python
  • sbcl
  • v8

Фактически реализацияТривиальный в Ruby довольно прост:

repl = -> prompt { print prompt; puts(" => %s" % eval(gets.chomp!)) }
loop { repl[">> "] }
1 голос
/ 03 ноября 2018

Чем Lisp REPL отличается от non-Lisp REPL?

Давайте сравним Common Lisp REPL с IPython Python.

Основные два пункта:

  • Lisp - это язык изображений.Нет необходимости перезапускать процесс / REPL / все приложение после изменения.Мы компилируем нашу функцию кода по функциям (с предупреждениями компилятора и т. Д.).
  • мы не теряем состояние.Более того, когда мы обновляем определения классов, наши объекты в REPL также обновляются в соответствии с правилами, которые мы контролируем.Таким образом, мы можем выполнить горячую перезагрузку кода в работающей системе.

Как правило, в Python вы запускаете IPython или попадаете в ipdb.Вы определяете некоторые данные, пока не опробуете свою новую функцию.Вы редактируете исходный код и хотите повторить попытку, поэтому вы выходите из IPython и снова запускаете весь процесс.В Лиспе (в основном Common Lisp) совсем нет, все более интерактивно.

...