После нескольких месяцев изучения и игры с Lisp, как с CL, так и с немного Clojure, я все еще не вижу веской причины что-либо писать в нем вместо C #.
Мне бы очень хотелось, чтобы были веские причины, или чтобы кто-то указывал, что мне не хватает чего-то действительно большого .
Сильные стороны Лиспа (согласно моим исследованиям):
- Компактная, выразительная запись - больше, чем C #, да ... но я, похоже, тоже могу выразить эти идеи в C #.
- Неявная поддержка функционального программирования - C # с методами расширения LINQ:
- mapcar =. Выбрать (лямбда)
- mapcan = .Select (лямбда) .Aggregate ((a, b) => a.Union (b))
- автомобиль / первый = .First ()
- cdr / rest = .Skip (1) .... и т. д.
- Поддержка лямбда-функции и функции высшего порядка - в C # есть это, и синтаксис, возможно, проще:
- "(лямбда (х) (тело))" против "х => (тело)"
- "# (" с "%", "% 1", "% 2" хорошо в Clojure
- Диспетчеризация методов отделена от объектов - в C # есть методы расширения
- Диспетчеризация мультиметода - C # не имеет этого изначально, но я мог бы реализовать это как вызов функции через несколько часов
- Код - это данные (и макросы). Возможно, я не «получил» макросы, но я не видел ни одного примера, где идея макроса не могла бы быть реализована как функция; это не меняет "язык", но я не уверен, что это сила
- DSLs - может сделать это только через композицию функций ... но это работает
- Нетипизированное «исследовательское» программирование - для структур / классов, автообъекты C # и «объект» работают достаточно хорошо, и вы можете легко перейти к более сильной типизации по мере продвижения
- Работает на оборудовании, отличном от Windows - Да, так? Вне колледжа я знал только одного человека, который не запускал Windows дома, или хотя бы виртуальную машину Windows на * nix / Mac. (Опять же, может быть, это важнее, чем я думал, и мне только что промыли мозги ...)
- REPL для восходящего дизайна - хорошо, я признаю, что это действительно очень приятно, и я скучаю по нему в C #.
Вещи, которые мне не хватает в Лиспе (из-за сочетания C #, .NET, Visual Studio, Resharper):
- Пространства имен. Даже со статическими методами мне нравится привязывать их к «классу», чтобы классифицировать их контекст (похоже, что у Clojure это есть, а CL нет).
- Отличная поддержка компиляции и разработки
- система типов позволяет мне определить «правильность» структур данных, которые я передаю
- все, что написано с ошибками, указывается в реальном времени Я не должен ждать до времени выполнения, чтобы знать
- улучшения кода (такие как использование подхода FP вместо императивного) выполняются автоматически
- Инструменты разработки графического интерфейса: WinForms и WPF (я знаю, что Clojure имеет доступ к библиотекам Java GUI, но они мне совершенно чужды).
- Инструменты отладки графического интерфейса пользователя: точки останова, пошаговое выполнение, пошаговое управление, инспекторы значений (текст, xml, пользовательские), отслеживание, отладка за потоком, условные точки останова, окно стека вызовов с возможностью перехода к коду на любом уровне. в стеке
- (Чтобы быть справедливым, моя работа с Emacs + Slime, казалось, обеспечила часть этого, но я неравнодушен к подходу, основанному на графическом интерфейсе VS)
Мне действительно нравится ажиотаж вокруг Lisp, и я дал ему шанс.
Но есть ли что-то, что я могу сделать в Лиспе, что я не могу сделать также в C #? Это может быть немного более многословно в C #, но у меня также есть автозаполнение.
Чего мне не хватает? Почему я должен использовать Clojure / CL?
AND
илиOR
как функцию. Это может быть сделано (учитываяLAMBDA
, что также является макросом), но я не вижу очевидного способа сделать это, который не будет полностью отстой.:
(или::
для неэкспортированных символов) для именования символов в пакетах, например, ваш пример здесь будет использоватьhttp:session
иchat:session
.Ответы:
Макросы позволяют писать компиляторы без комфорта вашего языка. DSL в таких языках, как C #, обычно представляют собой интерпретатор времени выполнения. В зависимости от вашего домена это может быть проблематично по соображениям производительности. Скорость имеет значение , иначе вы будете кодировать на интерпретируемом языке, а не на C #.
Кроме того, макросы также позволяют учитывать человеческий фактор - какой синтаксис наиболее удобен для конкретного DSL? С C # мало что можно сделать с синтаксисом.
Таким образом, Lisp позволяет вам участвовать в разработке языка без ущерба для эффективности . И хотя эти проблемы могут не иметь для вас значения , они чрезвычайно важны, поскольку они лежат в основе всех полезных языков программирования, ориентированных на производство. В C # этот фундаментальный элемент в лучшем случае представлен вам в очень ограниченной форме.
Важность макросов не теряется на других языках - на ум приходит шаблон Haskell, camlp4. Но опять же, это проблема юзабилити - макросы Lisp на сегодняшний день, вероятно, являются наиболее удобной, но мощной реализацией преобразования во время компиляции.
Итак, в общем то, что люди обычно делают с такими языками, как C #, это сборка DSIL (предметно-ориентированных интерпретируемых языков). Lisp дает вам возможность создать что-то гораздо более радикальное, DSP (предметно-ориентированные парадигмы). Racket (ранее PLT-схема) особенно вдохновляет в этом отношении - у них есть ленивый язык (Lazy Racket), типизированный (Typed Racket), Prolog и Datalog, все идиоматически внедренные с помощью макросов. Все эти парадигмы предоставляют мощные решения для больших классов проблем - проблем, которые не могут быть решены с помощью императивного программирования или даже парадигм FP.
источник
К счастью, почти все, что изначально было доступно только в Лиспе, перешло на многие другие современные языки. Самым большим исключением является философия «код - данные» и макросы.
Да, макросы трудно понять. В общем, метапрограммирование трудно осуществить. Если вы видели какой-либо макрос, который мог бы быть реализован как функция, то программист делал это неправильно. Макросы следует использовать только тогда, когда функция не работает.
Пример макроса "hello world" - написать "if" в терминах cond. Если вы действительно заинтересованы в изучении возможностей макросов, попробуйте определить «my-if» в clojure, используя функции, и посмотрите, как оно ломается. Я не хочу быть таинственным, я просто никогда не видел, чтобы кто-то начинал понимать макросы только по объяснениям, но это слайд-шоу довольно хорошее вступление: http://www.slideshare.net/pcalcado/lisp-macros-in -20-й-отличая-Clojure-презентация
У меня были небольшие проекты, где я сохранял буквально сотни / тысячи строк кода с помощью макросов (по сравнению с тем, что было бы в Java). Большая часть Clojure реализована в Clojure, что возможно только благодаря макросистеме.
источник
(defun my-if (test then &optional else) (cond (test (funcall then)) ('otherwise (funcall else))))
Я не эксперт в Common Lisp, но я достаточно хорошо знаю и C #, и Clojure, так что, надеюсь, это полезная перспектива.
Благодаря невероятно простому синтаксису философия Lisp «код - данные» гораздо мощнее, чем все, что вы можете сделать с помощью композиции функций / шаблонов / обобщений и т. Д. Это больше, чем просто DSL, это генерация кода и манипулирование во время компиляции , как фундаментальная особенность языка. Если вы попытаетесь получить такого рода возможности в негомоикональной форме языке, таком как C # или Java, в конечном итоге вы в конечном итоге будете плохо изобретать Lisp. Отсюда и известное десятое правило Гринспуна: «Любая достаточно сложная программа на C или Fortran содержит специальную, неформально определенную, медленную реализацию половины Common Lisp». Если вам когда-либо приходилось изобретать в своем приложении язык шаблонов / управления потоком, полный Тьюринга, то вы, вероятно, знаете, что я имею в виду .....
Параллельность - это уникальная сила Clojure (даже по сравнению с другими Лиспами), но если вы считаете, что будущее программирования будет означать необходимость писать приложения, которые масштабируются на многоядерные машины, то вам действительно стоит задуматься об этом - это довольно новаторским. Clojure STM (программная транзакционная память) работает, потому что Clojure включает конструкции параллельности с неизменяемостью и без блокировки, основанные на новом разделении идентичности и состояния (см . Превосходное видео Рича Хики об идентичности и состоянии ). Было бы очень больно прививать такую возможность параллелизма в среде языка / среды выполнения, где значительная часть API работает с изменяемыми объектами.
Функциональное программирование - Лисп подчеркивает программирование с функциями более высокого порядка. Вы правы в том, что его можно эмулировать в объектно-ориентированных объектах с замыканиями / объектами функций и т. Д., Но разница в том, что весь язык и стандартная библиотека разработаны, чтобы помочь вам писать код таким образом. Например, последовательности Clojure являются ленивыми , поэтому могут быть бесконечно длинными в отличие от ваших ArrayLists или HashMaps в C # / Java. Это делает некоторые алгоритмы намного проще для выражения, например, следующий реализует бесконечный список чисел Фибоначчи:
(def fibs (lazy-cat '(0 1) (map + fibs (drop 1 fibs))))
Динамическая типизация - Лиспы, как правило, находятся на «динамическом» конце спектра функциональных языков программирования (в отличие от языков, подобных Haskell, с очень сильной и красивой концепцией статических типов). Это имеет как преимущества, так и недостатки, но я бы сказал, что динамические языки имеют тенденцию к повышению производительности программирования из-за большей гибкости. Такая динамическая типизация требует незначительных затрат производительности во время выполнения, но если это вас беспокоит, вы всегда можете добавить подсказки типов в свой код, чтобы получить статическую типизацию. По сути - вы получаете удобство по умолчанию и производительность, если вы хотите проделать дополнительную работу, чтобы добиться этого.
Интерактивная разработка - на самом деле я думаю, что вы можете получить что-то вроде REPL для C # 4.0, но в Лиспе это стандартная практика, а не новинка. Вам совсем не нужно делать цикл компиляции / сборки / тестирования - вы пишете свой код непосредственно в работающей среде и только позже копируете его в исходные файлы. Он учит вас совершенно другому способу кодирования, где вы сразу видите результаты и итеративно дорабатываете свое решение.
Несколько быстрых комментариев о вещах, которые вы видите как «отсутствующие»:
Лично я нахожу преимущества Clojure / Lisp довольно убедительными, и я не планирую возвращаться к C # / Java (помимо того, что мне нужно заимствовать все полезные библиотеки!).
Однако мне потребовалось несколько месяцев, чтобы по-настоящему обдумать все это. Если вы искренне заинтересованы в изучении Lisp / Clojure как полезного учебного процесса, ничто не заменит погружения и принуждения себя к реализации нескольких вещей .....
источник
C # имеет много функций, но смесь выглядит по-другому. То, что у некоторого языка есть особенность, еще не означает, что он прост в использовании, парадигматичен и хорошо интегрирован с остальным языком.
Common Lisp имеет такой микс:
Теперь и другие языки, такие как C #, тоже это имеют. Но часто не с тем же акцентом.
C # имеет
Не поможет, например, то, что в C # есть функции манипулирования кодом, если они не так широко используются, как в Lisp. В Лиспе вы не найдете много программ, которые не используют макросы интенсивно во всех видах простых и сложных способов. Использование манипулирования кодом действительно большая особенность в Лиспе. Эмуляция некоторых применений возможна во многих языках, но она не делает ее центральной функцией, как в Lisp. Для примера смотрите книги типа «На Лиспе» или «Практический общий Лисп».
То же самое для интерактивной разработки. Если вы разрабатываете большую систему САПР, большая часть разработки будет интерактивной из редактора и из REPL - непосредственно в работающее приложение САПР. Вы можете эмулировать большую часть этого в C # или других языках - часто путем реализации языка расширения. Для Lisp было бы глупо перезапускать разрабатываемое приложение CAD для добавления изменений. В этом случае система САПР будет также содержать расширенный Лисп, в который будут добавлены языковые функции (в форме макросов и символических описаний) для описания объектов САПР и их взаимосвязей (частично, содержаться, ...). Можно делать аналогичные вещи в C #, но в Lisp это стиль разработки по умолчанию.
Если вы разрабатываете сложное программное обеспечение, используя интерактивный процесс разработки, или ваше программное обеспечение имеет значительную символическую часть (метапрограммирование, другие парадигмы, компьютерная алгебра, музыкальная композиция, планирование, ...), тогда Lisp может быть для вас.
Если вы работаете в среде Windows (я не работаю), то у C # есть преимущество, так как это основной язык разработки Microsoft. Microsoft не поддерживает какую-либо форму Lisp.
Ты пишешь:
Common Lisp не присоединяет пространства имен к классам. Пространства имен предоставляются пакетами символов и не зависят от классов. Вам необходимо переориентировать свой подход к объектно-ориентированному программированию, если вы используете CLOS.
Используйте компилятор Lisp. Он информирует вас о неизвестных переменных, может иметь рекомендации по стилю (в зависимости от используемого компилятора), дает подсказки по эффективности, ... Компилятор нажатий клавиш.
Коммерческие Lisps, такие как LispWorks и Allegro CL, предлагают аналогичные вещи под Windows.
Коммерческие Lisps, такие как LispWorks и Allegro CL, предлагают все это под Windows.
источник
Ренье Йосвиг сказал это лучше всего.
Для меня всю мощь Lisp невозможно понять, просто взглянув на список возможностей Lisp. В частности, для Лисп и Common Lisp целое больше, чем сумма частей. Это не просто REPL, s-выражения, макросы, отправка по нескольким методам, замыкания, пакеты, перезапуски CLOS, плюс X, Y и Z. Это весь гениальный интеграция. Это повседневный опыт, который очень сильно отличается от повседневного опыта других языков программирования.
Лисп выглядит по-другому, он используется по-разному, при его использовании можно по-другому подходить к программированию. Это элегантный, полный, большой и цельный. Это не без его собственных пятен и peccadilloes. Есть, конечно, сравнения с другими языками, которые могут быть сделаны там, где они не могут выйти вперед. Но Lisp ни в коем случае не является просто языком X с REPL и макросами или языком Y с мульти-методической диспетчеризацией и перезапуском.
источник
Как правило, макрос должен использоваться для чего-то, что не может быть выражено как вызов функции. Я не знаю, есть ли в C # условное переключение (подобное тому, что существует в C, как switch (form) {case ...}), но давайте предположим, что оно имеет и имеет почти те же ограничения ( требующий интегрального типа).
Теперь давайте предположим, что вам нужно что-то похожее на это, но отправляющее по строкам. В lisp (ну, в частности, в Common Lisp и, возможно, в других), это «просто» макрос, и если вы ограничиваете пользователя наличием постоянных строк (вероятно, не изнурительных) для сравнения, вы можете (время компиляции) вычислить минимальная последовательность тестов, чтобы определить, какой регистр соответствует (или использовать хеш-таблицу, возможно, для хранения замыканий).
Хотя может быть возможно выразить это как функцию (строка сравнения, список случаев и замыканий для выполнения), она, вероятно, не будет выглядеть как «обычный код», и именно здесь макросы lisp имеют определенный выигрыш. Вы , вероятно , использовали довольно много макросов или специальные формы TAHT являются макро-как, как
defun
,defmacro
,setf
,cond
,loop
и многое другое.источник
Это лучшая объяснительная статья, которую я нашел, которая берет читателя с поверхности адвокации Lisp, чтобы показать некоторые из более глубоких последствий используемых концепций:
http://www.defmacro.org/ramblings/lisp.html
Я помню, как читал в другом месте идею, подобную этой: другие языки приняли различные функции Lisp; сборка мусора, динамическая типизация, анонимные функции, замыкания для создания лучшего C ++ / C / Algol. Однако, чтобы получить полную мощность Lisp, вам нужны все его основные функции, и в этот момент у вас есть еще один диалект Lisp, отдельное семейство языков, которое существует в той или иной форме более 50 лет.
источник