Схема против Common Lisp: Какие характеристики повлияли на ваш проект? [закрыто]

155

В StackOverflow и на этом сайте нет недостатка в расплывчатых вопросах "Scheme vs Common Lisp", поэтому я хочу сделать этот вопрос более сфокусированным. Вопрос для людей, которые закодировали на обоих языках:

Во время написания кода на Схеме, какие конкретные элементы вашего опыта в Common Lisp вам больше всего не хватало? Или, наоборот, при кодировании в Common Lisp, что вы упустили из кодирования в Scheme?

Я не обязательно имею в виду только языковые особенности. Следующее - все действительные вещи, чтобы пропустить, что касается вопроса:

  • Конкретные библиотеки.
  • Особенности сред разработки, таких как SLIME, DrRacket и др.
  • Особенности конкретных реализаций, такие как способность Gambit записывать блоки кода на C прямо в ваш источник Scheme.
  • И конечно, языковые особенности.

Примеры ответов, на которые я надеюсь:

  • «Я пытался реализовать X в Common Lisp, и если бы у меня были первоклассные продолжения Scheme, я бы просто сделал Y, но вместо этого я должен был сделать Z, что было больше боли».
  • «Сценарии процесса сборки в моем проекте Scheme становились все более болезненными, так как мое дерево исходных текстов росло, и я связывал все больше и больше библиотек C. Для моего следующего проекта я вернулся в Common Lisp».
  • «У меня есть большая существующая кодовая база C ++, и для меня, возможность встраивать вызовы C ++ непосредственно в мой код Scheme Gambit полностью стоил любых недостатков, которые Scheme может иметь по сравнению с Common Lisp, даже включая отсутствие поддержки SWIG».

Итак, я надеюсь на военные истории, а не на общие настроения, такие как «Схема - более простой язык» и т. Д.

SuperElectric
источник
25
Отличный, хорошо сформулированный вопрос. Мне самому интересно это; Надеемся, что есть люди с опытом работы на обоих языках, которые хотят дать некоторое представление.
Роберт Харви
1
@ Джош К - это однозначно ответ, но нет однозначного ответа. За исключением того, что я держал пари, что будет один, потому что кто-то выйдет с ответом, который настолько удивителен, что все будут как воу!
Гленатрон
4
@Josh: Тогда, возможно, вы не знакомы со Scheme и Common Lisp. Оба языка очень сильны сами по себе, но ни один из них не получил широкого распространения. Почему это? Это может быть потому, что есть так много диалектов; какой из них вы выбираете? Подобное сравнение может быть очень полезным, и ФП тщательно сформулировал вопрос, чтобы ограничить сферу ответами, которые, на мой взгляд, являются очень конкретными и подотчетными.
Роберт Харви
13
Ребята, не закрывайте вопрос только потому, что он вам не нравится или не может быть с ним связан. Это явно "настоящий" вопрос; если вы не можете найти более вескую причину, чтобы закрыть его, вам не следует голосовать за закрытие.
Роберт Харви
4
Вы можете написать Ричарду Столлману его ответ.
Wassimans

Ответы:

100

Я получил степень бакалавра в области когнитивной науки и искусственного интеллекта. После этого у меня было введение из одного курса в Lisp. Я думал, что язык интересен (как в «элегантном»), но не особо задумывался о нем, пока не наткнулся на Десятое Правило Гринспуна гораздо позже:

Любая достаточно сложная программа на C или Fortran содержит специальную, неформально определенную, медленную реализацию половины Common Lisp.

Гринспен утверждал (частично), что многие сложные программы имеют встроенные интерпретаторы. Вместо того, чтобы встроить интерпретатор в язык, он предположил, что было бы более разумно использовать язык, подобный Лиспу, который уже имеет встроенный интерпретатор (или компилятор).

В то время я работал над довольно большим приложением, которое выполняло пользовательские вычисления с использованием специального интерпретатора для пользовательского языка. Я решил попробовать переписать его ядро ​​на Лиспе в качестве масштабного эксперимента.

Это заняло примерно шесть недель. Оригинальный код был ~ 100 000 строк Delphi (вариант Паскаля). В Лиспе это было сокращено до ~ 10000 строк. Еще более удивительным было то, что двигатель Lisp работал в 3-6 раз быстрее. И имейте в виду, что это была работа новичка Лиспа! Весь этот опыт был довольно откровением для меня; Впервые я увидел возможность сочетать производительность и выразительность на одном языке.

Некоторое время спустя, когда я начал работать над веб-проектом, я прослушал несколько языков. Я включил в смесь Lisp и Scheme. В конце концов я выбрал реализацию схемы - схему Chez . Я был очень доволен результатами.

Веб-проект представляет собой высокопроизводительный «механизм выбора» . Мы используем Scheme разными способами, от обработки данных до запроса данных и генерации страниц. Во многих местах мы фактически начинали с другого языка, но в итоге перешли на Scheme по причинам, которые я кратко опишу ниже.

Теперь я могу ответить на ваш вопрос (хотя бы частично).

Во время прослушивания мы рассмотрели различные реализации Lisp и Scheme. Со стороны Lisp мы рассмотрели (я полагаю) Allegro CL, CMUCL, SBCL и LispWorks. Со стороны Схемы мы смотрели (я полагаю) Биглоо, Курица, Чез, Гамбит. (Выбор языка был давным-давно; поэтому я немного туманен. Я могу выкопать некоторые заметки, если это важно.)

Мы сразу искали: а) нативные темы и б) поддержку Linux, Mac и Windows. Эти два условия объединили всех, кроме (я думаю) Аллегро и Чеза - поэтому, чтобы продолжить оценку, нам пришлось ослабить требование многопоточности.

Мы собрали набор небольших программ и использовали их для оценки и тестирования. Это выявило ряд проблем. Например: некоторые реализации имели дефекты, которые мешали выполнению некоторых тестов до завершения; некоторые реализации не могут компилировать код во время выполнения; некоторые реализации не могли легко интегрировать скомпилированный код времени выполнения с предварительно скомпилированным кодом; у некоторых реализаций были сборщики мусора, которые были явно лучше (или явно хуже), чем у других; и т.п.

Для наших нужд только три коммерческие реализации - Allegro, Chez и Lispworks - прошли наши первичные испытания. Из трех только Chez прошел все тесты с летающими цветами. В то время я думал, что у Lispworks не было собственных потоков на какой-либо платформе (я думаю, что они есть сейчас), и я думаю, что у Allegro были только собственные потоки на некоторых платформах. Кроме того, у Allegro была плата за лицензию «позвони нам», что мне не очень понравилось. Я считаю, что у Lispworks не было платы за время выполнения, а у Chez была простая (и очень разумная) схема (и она вступила в силу только в том случае, если вы использовали компилятор во время выполнения).

Произведя несколько значительных кусков кода как на Лиспе, так и на Схеме, вот некоторые точки сравнения и сравнения:

  • Среды Лиспа гораздо более зрелые. Вы получаете намного больше за доллар. (Сказав это, больше кода также означает больше ошибок.)

  • Среды Лиспа гораздо сложнее изучать. Вам нужно гораздо больше времени, чтобы стать опытным; Common Lisp - это огромный язык - и это прежде, чем вы перейдете к библиотекам, которые коммерческие реализации добавляют к нему. (Сказав это, синтаксический случай Scheme гораздо более тонкий и сложный, чем любая другая вещь в Lisp.)

  • В средах Lisp может быть несколько сложнее создавать двоичные файлы. Вам нужно «встряхнуть» ваше изображение, чтобы удалить ненужные биты, и если вы не будете правильно выполнять свою программу во время этого процесса, вы можете позже столкнуться с ошибками во время выполнения. , Напротив, с Chez мы компилируем файл верхнего уровня, который включает в себя все остальные файлы, которые ему нужны, и мы закончили.

Я уже говорил, что в конечном итоге мы использовали Scheme во многих местах, которые мы изначально не собирались делать. Почему? Я могу думать о трех причинах из головы.

Сначала мы научились доверять Chez (и его разработчику, Cadence). Мы много просили от инструмента, и он постоянно доставлял. Например, Chez исторически имел незначительное количество дефектов, и его менеджер памяти был очень, очень хорош.

Во-вторых, мы научились любить представление, которое мы получили от Chez. Мы использовали что-то похожее на язык сценариев - и мы получили скорость нативного кода от этого. Для некоторых вещей это не имело значения - но это никогда не ранило, а иногда это очень помогало.

В-третьих, мы научились любить ту схему, которую могла предоставить абстракция. Кстати, я имею в виду не только макросы; Я имею в виду такие вещи, как замыкания, лямбды, хвостовые вызовы и т. Д. Как только вы начинаете думать в этих терминах, другие языки кажутся довольно ограниченными по сравнению.

Схема идеальна? Нет; это компромисс. Во-первых, это позволяет отдельным разработчикам быть более эффективными - но разработчикам труднее ухватиться за код друг друга, потому что в Схеме отсутствуют указатели, которые есть в большинстве языков (например, для циклов) (например, существует миллион способов сделать это). для цикла). Во-вторых, гораздо меньше разработчиков, с которыми можно общаться, брать на работу, брать кредиты и т. Д.

Подводя итог, я думаю, я бы сказал: Lisp и Scheme предлагают некоторые возможности, не так широко доступные где-либо еще. Эта возможность является компромиссом, поэтому лучше иметь такую, которая имеет смысл в вашем конкретном случае. В нашем случае определяющие факторы между тем, использовать ли Lisp или Scheme, были в большей степени связаны с очень фундаментальными функциями (поддержка платформы, потоки платформы, компиляция во время выполнения, лицензирование во время выполнения), чем с возможностями языка или библиотеки. Опять же, в нашем случае это тоже было компромиссом: с Chez мы получили основные функции, которые хотели, но мы потеряли обширные библиотеки, имеющиеся в коммерческих средах Lisp.

Также, чтобы повторить: мы смотрели на различные Лиспы и Схемы очень давно; с тех пор все они развивались и совершенствовались.

Майкл Ленаган
источник
1
Вау, это был действительно ужасный код Delphi, если он каким-то образом мог выполнять в 3-6 раз медленнее, чем реализация на Лиспе! :(
Мейсон Уилер
2
+1: Самое интересное в этом посте - это тот факт, что вы перешли с Lisp на Scheme после того, как сделали крупный проект на Lisp. (Или, может быть, я слишком долго скрывался на comp.lang.lisp.)
Ларри Коулман,
25
«Ого, это был действительно ужасный код Delphi, если он каким-то образом мог выполнять в 3-6 раз медленнее, чем реализация на Лиспе!» Правильно, я буду считать это моей неудачей за то, что я не объяснил это лучше. Реализация Lisp смогла преобразовать пользовательские выражения в выражения Lisp - тривиально простой процесс - и затем скомпилировать выражения Lisp в нативный код (с полной оптимизацией). В этом смысл десятого правила Гринспуна.
Майкл Ленаган,
1
Фантастический ответ! Я выберу его, по крайней мере, пока не появится лучший :) Один вопрос: вы говорите, что приняли решение использовать Chez Scheme, основываясь на состоянии месторождения "давным-давно". Не могли бы вы указать год?
SuperElectric
11
То, что реализация LISP может свободно скомпилировать что-то для машинного кода, а не полагаться на интерпретатор, является тонким и довольно полезным. В книге «Let Over Lambda» отмечается, что именно поэтому портативный пакет Common LISP regexp, клонирующий синтаксис регулярного выражения PERL, значительно превосходит PERL. В PERL внизу есть интерпретатор регулярных выражений. Пакет Common LISP компилирует регулярные выражения в код.
Джон Р. Штром,
37

Я обычно не люблю вставлять ссылку в качестве ответа, но я написал статью в блоге именно об этом. Это не является исчерпывающим, но это дает понять некоторые основные моменты.

http://symbo1ics.com/blog/?p=729

Редактировать : вот основные моменты:

  1. СУЩЕСТВОВАНИЕ : Обе россыпи последовали за кучей других огрызков. Схема взяла минимальный, аксиоматический маршрут. CL взял барочный маршрут.
  2. СЛУЧАЙ : Обычно схема чувствительна к регистру. CL нет (хотя это может быть). Это иногда упускается, но его практичность обсуждается (мной).
  3. ИМЕНА : Имена символов в CL много раз нечетные и запутанные. TERPRI, PROGNИ т.д. Схема обычно имеет очень осмысленные имена. Это что-то упущено в CL.
  4. ФУНКЦИИ : CL имеет отдельное пространство имен функций. Это не пропущено в схеме. Наличие единого пространства имен обычно обеспечивает очень чистое функциональное программирование, которое часто сложно или неудобно в CL. Но это обходится дорого - вам иногда приходится запутывать такие имена, как " list" to " lst" в Схеме.
  5. МАКРОС : Я больше всего скучаю по грязным макросам низкого уровня в Схеме. Да, syntax-rulesвсе нормально и круто, пока ты не захочешь по-настоящему взломать некоторые вещи. С другой стороны, гигиенические макросы иногда пропускаются в CL. Отсутствие стандартного способа сделать это означает заново изобретать колесо.
  6. ПОРТАТИВНОСТЬ : часто бывает так, что CL более переносим, ​​несмотря на стандартизацию обоих языков. CL больше, и поэтому есть больше стандартных функций для использования без внешних библиотек. Это также означает, что больше зависящих от реализации вещей может быть выполнено переносимо. Кроме того, у Scheme триллион реализаций, большинство из которых несколько несовместимы. Это делает CL очень желательным.
  7. БИБЛИОТЕКИ : Очень связано с моим последним пунктом. Схема имеет SRFIs, но не является общепризнанным. Нет портативного способа работы с библиотеками. С другой стороны, у CL есть способы. А Quicklisp - это дар бога (Xach) - своего рода хранилище библиотек для использования.
  8. ОСУЩЕСТВЛЕНИЕ : Схема страдает от стольких реализаций. Реальной канонической реализации не существует. CL, с другой стороны, имеет несколько очень хороших высокопроизводительных или специфических реализаций (высокая производительность: SBCL, коммерческая: Allegro, встроенная: ECL, портативная: CLISP, Java: ABCL, ...).

Хотя я говорил от первого лица чуть выше, должно быть ясно, что я скучаю, а что нет.

[Я извиняюсь, если они слишком общие. Кажется, вам может понадобиться более конкретная информация. В этом посте есть некоторые особенности.]

Quadrescence
источник
как насчет (действительно) краткого тизера? ^^
Дейв О.
2
Пожалуйста, укажите основные моменты. Ответы должны быть самостоятельными.
1
@Dave O. и @ Thorbjørn Ravn Andersen: добавлено резюме по запросу. Благодарю.
Quadrescence
2
«Барочный маршрут»! Какой отличный способ выразить это.
Марк C
Common Lisp чувствителен к регистру, но преобразует его ввод в верхний регистр, прежде чем оценивать его. Вы можете получить строчные буквы в символах, цитируя их. Проблема с именем связана с тем, что Scheme избавился от старых плохих имен, а CL - нет.
Дэвид Торнли
25

Недавно я начал домашний проект, используя библиотеку, которая имеет версию C и версию Java. Я хотел использовать Lisp для проекта и потратил около месяца на колебания между использованием Common Lisp, Scheme или Clojure. У меня есть некоторый опыт работы со всеми тремя, но только с игрушечными проектами. Я расскажу вам немного о своем опыте с каждым из них, прежде чем рассказать, какой из них я выбрал.

PLT Racket имеет хорошую IDE, которая не только позволяет вам оценивать выражения из редактора, но также позволяет вводить скобки вместо скобок, переключая их обратно на скобки, где это уместно. Racket также имеет большой набор библиотек с возможностью установки и даже более доступных для загрузки. Визуальный отладчик также полезен.

Моя реализация Common Lisp (SBCL) не имеет IDE, но обычно в реализациях CL с открытым исходным кодом используется Emacs и SLIME. Эта комбинация может быть очень эффективной. Наряду с возможностью оценивать выражения по мере их ввода в исходный файл, есть также REPL, в котором есть все доступные для редактирования команды emacs, поэтому копирование кода может эффективно выполняться в обоих направлениях. Даже объекты, отображаемые в буфере REPL, можно копировать и вставлять. Alt+(и Alt+)эффективны для работы с соответствующими скобками и отступами.

Все вышеперечисленные функции Emacs также доступны для Clojure. Мой опыт редактирования с Clojure похож на тот, что был в Lisp. Java-взаимодействие работало нормально, и я хотел бы сделать проект Clojure, как только он станет зрелым.

Я смог получить доступ к библиотеке, используя все три (Common Lisp, Racket и Clojure), но в итоге я выбрал Common Lisp для проекта. Решающим фактором было то, что FFI было намного проще использовать в Common Lisp. У CFFI есть очень хорошее руководство с примером кода и подробными объяснениями каждого метода. Я был в состоянии обернуть 20 функций C днем, и с тех пор мне не приходилось трогать код.

Другим фактором было то, что я больше знаком с Common Lisp, чем с Clojure или R6RS Scheme. Я прочитал большинство книг Практического Общего Лиспа и Грэма, и я доволен Hyperspec. Это еще не очень «шустрый» код, но я уверен, что он изменится, когда я приобрету больше опыта.

Ларри Коулман
источник
Спасибо за детали! Правильно ли я вас понимаю, что вы думали, что FFI SBCL проще в использовании, чем Clojure? Если это так, я был бы очень удивлен этим, учитывая, что вы можете просто вызывать методы Java непосредственно из Clojure без необходимости оборачивать их. (Или вам также нужно было вызывать собственный код?)
SuperElectric
6
@SuperElectric: вызов «встроенных» методов Java из Clojure тривиален; вызов методов Java, находящихся в загруженной библиотеке: не так уж и много. Я действительно потратил больше времени на то, чтобы правильно настроить путь к классам и импорту, чем на то, чтобы заставить мой первый метод C работать с SBCL и CFFI. Но я не эксперт по Java, поэтому ваш пробег может меняться.
Ларри Коулман
21

Я программирую на CL и Racket.

Сейчас я разрабатываю веб-сайт в Common Lisp и написал набор собственных программ для моего предыдущего работодателя в Racket.

Для внутреннего кода я выбрал Racket (тогда он назывался PLT Scheme), потому что работодателем был магазин Windows, и я не мог заставить их платить за LispWorks. Единственной хорошей реализацией CL с открытым исходным кодом для Windows была (и остается) CCL, для которой требуется поддержка SSE в процессоре. Работодатель, будучи дешевым, использовал оборудование каменного века. Даже если у работодателя действительно было приличное оборудование, единственной библиотекой графического интерфейса в Common Lisp является McCLIM, который работает только в Unix. Racket имеет хорошую библиотеку графического интерфейса, которая работает как в Unix, так и в Windows, что имело решающее значение для успеха моего проекта.

Я потратил больше года на то, чтобы смириться с примитивным редактором DrRacket. EMACS не смог превратить версию Racket с графическим интерфейсом, которая тогда называлась MrEd, в неполный список ошибок в Windows. Мне пришлось обойтись без возможности оценить выражение на курсоре одним нажатием клавиши. Вместо этого мне пришлось вручную выбрать S-выражение, скопировать его, щелкнуть окно REPL (потому что на него не нужно нажимать клавиши), а затем вставить S-выражение. Мне также пришлось обходиться без редактора, который мог бы показать ожидаемые аргументы функции или макроса, который я использовал. DrRacket не заменит SLIME.

Работодатель использовал закрытую базу данных со сложным XML-API, который требовал загрузки на первый взгляд ненужной информации, чтобы иметь возможность ответить на свою версию запроса SELECT. Я решил использовать HTMLPrag как для передачи XML в этот API, так и для анализа ответов. Это работало отлично.

Мне пришлось изучить сверхсложную макросистему Racket «синтаксис-регистр», чтобы написать макрос, который позволял бы мне взаимодействовать с чрезмерно сложным XML API, печатая формы, похожие на SQL. Эта часть была бы намного проще, если бы в моем распоряжении был DEFMACRO. Тем не менее, конечный результат был по-прежнему без шва, хотя потребовалось больше усилий для достижения.

Кроме того, мне пришлось обходиться без макроса LOOP от Common Lisp. Racket начал предоставлять альтернативу только после того, как я написал большую часть кода, и альтернатива по-прежнему отстой по сравнению с LOOP (хотя команда разработчиков Racket настаивает, что она лучше - они просто ошибаются). Я закончил тем, что написал много именованных форм LET, которые использовали «car» и «cdr» для перебора списков.

Говоря об автомобиле и CDR, нет ничего более расстраивающего, чем интерпретация Схемы (car '()) как ошибки. Я воспользовался чувствительностью к регистру Racket и реализовал CAR и CDR, которые имеют семантику Common Lisp. Однако разделение '() и #f делает гораздо менее полезным возвращение' () в качестве значения по умолчанию.

Я также в конечном итоге повторно внедрил UNWIND-PROTECT и изобрел свою собственную систему перезапуска, чтобы заполнить пробел, оставленный Racket. Сообщество Racket должно понять, что перезапуски очень полезны и просты в реализации.

Форма значений Racket была слишком многословна, поэтому я применил MULTIPLE-VALUE-BIND. Это было абсолютно необходимо, потому что Racket требует , чтобы вы получили все сгенерированные значения, используете ли вы их или нет.

Позже я попытался написать клиент eBay XML API в Common Lisp, но обнаружил, что у него нет ничего похожего на HTMLPrag. HTMLPrag чертовски полезен. Я закончил делать этот проект в Racket. Я экспериментировал со средствами Racket Literate Programming, только чтобы обнаружить, что я единственный программист на Земле, который считает правильно написанный грамотный код сложнее редактировать, чем обычный код, или неправильно написанный грамотный код с «чрезмерным количеством комментариев».

Мой новый проект выполняется в Common Lisp, и это был правильный выбор, потому что сообщество Racket просто не верит в параллелизм, который необходим для этого проекта. Единственное, что я думал, что мог пропустить из Ракетки, это продолжения. Тем не менее, я смог сделать то, что мне было нужно, с помощью перезапусков, и, оглядываясь назад, возможно, мог бы сделать это с помощью простого закрытия.

рэкетир
источник
2
Я сам не пробовал, но видел сообщения в блоге от людей, которые используют программу для командной строки с Emacs. Например: bc.tech.coop/scheme/scheme-emacs.htm
Ларри Коулман,
5
Честно говоря, звучит так, будто вы пришли в Scheme, желая написать CL вместо того, чтобы пытаться приблизиться к вещам из идиоматического Scheme POV. Например, разве схема не поощряет рекурсию, а не использование циклов?
Сани
Схема @ArtB не просто поощряет рекурсию, она требует ее, поэтому, конечно, в упомянутом выше проекте использовалось много рекурсии. И это только добавило повторения (например, вы должны включать копию рекурсивного вызова в каждую ветвь condформы) и ошибки (правильно ли я написал тест завершения цикла в этот раз?) Даже сегодня у меня складывается впечатление эта ракетка в основном предназначена для студентов, а не профессиональных программистов. Каждый раз, когда я слышу о ком-то, кроме меня, они используют подъязык «Начинающий студент», и это для класса.
Сбросить счет
Если вы повторяете код через COND, вы говорите, что вам просто нужна другая функция?
Сани
@ArtB Функция для вызова функции цикла с разными аргументами? Это было бы бессмысленно. Вы видите такое повторение практически в любом коде Схемы. Есть даже примеры в собственном исходном коде Racket.
Сбросить счет
5

Схема разработана с учетом отдельной компиляции. Как следствие, мощность его макросов часто сильно ограничена, даже с расширениями, которые допускают дефмакро в стиле Common Lisp вместо плохой, ограничивающей гигиенической макросистемы. Не всегда возможно определить макрос, который определяет другой макрос, предназначенный для немедленного использования в следующей строке кода. И такая возможность необходима для реализации эффективных компиляторов eDSL.

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

К счастью, есть реализации Scheme (например, Racket), которые не имеют этого ограничения.

SK-логика
источник
1
Привет, я только начал мокрые ноги от Схемы в последнее время с помощью ракетки. Не могли бы вы привести быстрый пример использования негигиенических макросов в Racket? Тип доступных макросов кажется одним из самых обсуждаемых между CL и Scheme.
orange80
@ orange80, один из подходов заключается в использовании docs.racket-lang.org/mzlib/mzlib_defmacro.html И, конечно, в режиме R6RS есть менее ограничительный способ.
SK-logic
@ SK-logic, что ты делаешь с макросами, которые так негигеничны?
Сани
1
@ArtB, я реализую eDSL как функции компилятора, которые могут многое сделать с их исходным AST. Гигиена - это общая неприятность при таком подходе. Вы можете посмотреть, как это работает: github.com/combinatorylogic/mbase
SK-logic