Как цикл чтения-оценки-печати Lisp отличается от цикла Python?


Я столкнулся со следующим заявление Ричарда Столлмана:

' когда вы запускаете систему Lisp, она входит в цикл read-eval-print. Большинство других языков не имеют ничего сравнимого с чтением, ничего сравнимого с eval, и ничего сопоставимого с печатью. Какие зияющие недостатки! -

теперь я очень мало программировал на Lisp, но я написал значительное количество кода на Python и недавно немного на Erlang. Мое впечатление было что эти языки также предлагают цикл read-eval-print, но Столлман не согласен (по крайней мере, о Python):

' Я просмотрел документацию Python после того, как люди сказали мне, что это было принципиально похоже на Lisp. Мой вывод заключается в том, что это не так. Когда вы начинаете Lisp, он делает "read", "eval" и "print", все из которых отсутствуют в Python.-

действительно ли существует фундаментальная техническая разница между циклами чтения-оценки-печати Lisp и Python? Можете ли вы дать примеры вещей, которые Lisp REPL делает легким и которые трудно сделать в Python?

4 57

4 ответа:

в поддержку позиции Столлмана Python не делает то же самое, что и типичные системы Lisp в следующих областях:

  • The read функция в Lisp считывает S-выражение, которое представляет собой произвольную структуру данных, которая может быть либо обработана как данные, либо оценена как код. Самая близкая вещь в Python читает одну строку, которую вам нужно будет разобрать самостоятельно, если вы хотите, чтобы она что-то значила.

  • The eval функция в Lisp может выполнять любой код Lisp. Элемент eval функция в Python оценивает только выражения, и нужны exec оператор для выполнения операторов. Но оба они работают с исходным кодом Python, представленным в виде текста, и вам нужно перепрыгнуть через кучу обручей, чтобы "оценить" Python AST.

  • The print функция в Lisp записывает S-выражение в точно такой же форме, что read можно. print в Python выводит что-то определенное по данным, которые вы пытаетесь напечатать, что, конечно, не всегда обратимо.

заявление Столлмана немного неискренне, потому что явно Python тут есть функции с именем точно eval и print, но они делают что-то другое (и уступают) тому, что он ожидает.

по-моему, питон тут есть некоторые аспекты, похожие на Lisp, и я могу понять, почему люди, возможно, рекомендовали Stallman смотреть в Python. Впрочем, как Пол Грэм рассуждает в том, что сделало Lisp другим, любой язык программирования, который включает в себя все возможности Lisp, также должен быть Лисп.

точка зрения Столлмана заключается в том, что не реализация явного "читателя" делает REPL Python калекой по сравнению с Lisps, потому что он удаляет важный шаг из процесса REPL. Reader-это компонент, который преобразует текстовый входной поток в память-подумайте о чем-то вроде синтаксического анализатора XML, встроенного в язык и используемого для обоих исходных кодов и для сведения. Это полезно не только для написания макросов (что теоретически возможно в Python с ast модуль), но также для отладки и самоанализа.

говорите, что вы заинтересованы в том, как incf реализована специальная форма. Вы можете проверить это следующим образом:

[4]> (macroexpand '(incf a))
(SETQ A (+ A 1)) ;

но incf может сделать гораздо больше, чем увеличение значений символов. Что именно он делает, когда его просят увеличить запись хэш-таблицы? Давайте посмотрим:

[2]> (macroexpand '(incf (gethash htable key)))
(LET* ((#:G3069 HTABLE) (#:G3070 KEY) (#:G3071 (+ (GETHASH #:G3069 #:G3070) 1)))
 (SYSTEM::PUTHASH #:G3069 #:G3070 #:G3071)) ;

здесь мы узнаем, что incf вызывает системныйputhash функция, которая является деталью реализации этого общего Лиспа система. Обратите внимание, как " принтер "использует функции, известные" читателю", такие как введение анонимных символов с #: синтаксис и ссылки на одни и те же символы в рамках расширенного выражения. Эмуляция такого рода проверки в Python была бы гораздо более подробной и менее доступной.

в дополнение к очевидным пользам на REPL, опытная польза шепелявости print и read в коде как простой и доступный инструмент для сериализации , сопоставимо с XML или json. В то время как Python имеет str функция, эквивалентная Lisp's print, ему не хватает эквивалент read, ближайший эквивалентeval. eval конечно объединяет два различные концепции, разбор и оценка, что приводит к проблемы такой и решения такой и это повторяющаяся тема на форумах Python. Это не было бы проблемой в Lisp именно потому, что читатель и оценщик являются чисто разделены.

наконец, расширенные возможности средства чтения позволяют программисту расширить язык таким образом, что даже макросы не могли бы иначе обеспечить. Прекрасным примером такого создания трудных вещей является the infix пакета Марк Кантровиц, реализация полнофункционального синтаксиса инфикса в качестве макроса чтения.

в системе на основе Lisp обычно разрабатывается программа во время ее запуска из REPL (read eval print loop). Таким образом, он интегрирует кучу инструментов: завершение, редактор, интерпретатор командной строки, отладчик,... По умолчанию это. Введите выражение с ошибкой - вы находитесь на другом уровне REPL с включенными командами отладки. Вы действительно должны сделать что-то, чтобы избавиться от этого поведения.

вы можете иметь два разных значения REPL концепция:

  • цикл печати Read Eval, как в Lisp (или нескольких других подобных языках). Он читает программы и данные, он оценивает и печатает данные результата. Python не работает таким образом. REPL Lisp позволяет вам работать непосредственно в мета-программировании, писать код, который генерирует (код), проверять расширения, преобразовывать фактический код и т. д.. Lisp имеет чтение/eval / print в качестве верхнего цикла. Python имеет что-то вроде readstring / evaluate/printstring в качестве сверху-петля.

  • интерфейс командной строки. Интерактивная оболочка. См., например, для IPython. Сравните это с общим Lisp в муть.

оболочка Python по умолчанию в режиме по умолчанию на самом деле не так мощна для интерактивного использования:

Python 2.7.2 (default, Jun 20 2012, 16:23:33) 
[GCC 4.2.1 Compatible Apple Clang 4.0 (tags/Apple/clang-418.0.60)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a+2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>> 

вы получили сообщение об ошибке, и все.

сравните это с CLISP REPL:

rjmba:~ joswig$ clisp
  i i i i i i i       ooooo    o        ooooooo   ooooo   ooooo
  I I I I I I I      8     8   8           8     8     o  8    8
  I  \ `+' /  I      8         8           8     8        8    8
   \  `-+-'  /       8         8           8      ooooo   8oooo
    `-__|__-'        8         8           8           8  8
        |            8     o   8           8     o     8  8
  ------+------       ooooo    8oooooo  ooo8ooo   ooooo   8

Welcome to GNU CLISP 2.49 (2010-07-07) <http://clisp.cons.org/>

Copyright (c) Bruno Haible, Michael Stoll 1992, 1993
Copyright (c) Bruno Haible, Marcus Daniels 1994-1997
Copyright (c) Bruno Haible, Pierpaolo Bernardi, Sam Steingold 1998
Copyright (c) Bruno Haible, Sam Steingold 1999-2000
Copyright (c) Sam Steingold, Bruno Haible 2001-2010

Type :h and hit Enter for context help.

[1]> (+ a 2)

*** - SYSTEM::READ-EVAL-PRINT: variable A has no value
The following restarts are available:
USE-VALUE      :R1      Input a value to be used instead of A.
STORE-VALUE    :R2      Input a new value for A.
ABORT          :R3      Abort main loop
Break 1 [2]> 

CLISP использует условие Lisp система для взлома отладчика REPL. Он представляет некоторые перезагрузки. В контексте ошибки новый REPL предоставляет расширенные команды.

давайте использовать :R1 перезагрузку:

Break 1 [2]> :r1
Use instead of A> 2
4
[3]> 

таким образом, вы получаете интерактивный ремонт программ и запусков выполнения...

интерактивный режим Python отличается от режима "чтение кода из файла" Python несколькими небольшими, важными способами, вероятно, присущими текстовому представлению языка. Python также не является гомойконическим, что заставляет меня называть его "интерактивным режимом", а не "циклом чтения-оценки-печати". Это в стороне, я бы сказал, что это больше разница в классе, чем разница в натуре.

теперь что-то tahtactually приближается к "разнице в роде", в файле кода Python, вы можете легко вставить пустые строки:

def foo(n):
  m = n + 1

  return m

если вы попытаетесь вставить идентичный код в интерпретатор, он будет считать функцию "закрытой" и жаловаться, что у вас есть голый оператор возврата в неправильном отступе. Это не происходит в (Common) Lisp.

кроме того, есть некоторые довольно удобные переменные удобства в Common Lisp (CL), которые недоступны (по крайней мере, насколько я знаю) в Python. И CL и Python имеют " значение последнего выражения" (* в CL,_ в Python), но CL также имеет ** (значение выражения перед последним) и *** (значение одного перед этим) и +,++ и +++ (сами выражения). CL также не различает выражения и операторы (по сути, все является выражением), и все это помогает построить гораздо более богатый опыт REPL.

как я сказал в начале, это больше разница в классе, чем разница в Рода. Но был пробел если бы между ними было только немного шире, это, вероятно, тоже было бы разницей в роде.