** Я перешел со среднего уровня. Новый адрес: kodare.net **

Обновление: во многих моментах, связанных с этой статьей, меня исправили. Прочтите мое продолжение.

Я должен начать с некоторой предыстории: самое важное, что вам нужно знать, это то, что я выздоровевший C ++ True Believer. Я тоже неофил (то есть меня всегда тянет к новым блестящим вещам). Так что у меня есть сильное желание чего-то лучше, и в то же время я очень устал, чтобы не попасть в ловушку фанатиков. Я видел разницу между этими крайностями и постоянно слежу за собой на предмет этих тенденций.

Я не пробовал другие Lisp, кроме Clojure, поэтому я могу делать общие выводы о Lisp в целом, основываясь на особенностях Clojure. Заранее приношу свои извинения и приветствую исправления. ОБНОВЛЕНИЕ: с тех пор мне сказали, что хотя бы в Common Lisp есть ключевые аргументы, которые проверяются во время компиляции.

Я довольно долго погрузился в Clojure, прежде чем почувствовал себя неловко. Мне потребовалось много времени, чтобы понять, откуда взялось это чувство. На своем опыте работы с C ++ я научился быстрее обнаруживать симптомы разочарования, но при использовании Lisps ловушка, на мой взгляд, более тонкая. В C ++ есть тенденция обвинять пользователя в проблемах, в то же время прославляя себя за то, что умудрился работать с таким неумолимым и «умным» языком. Привлекательность отождествления себя с «умным человеком» очень сильна. А работать ближе к машине тоже круто, даже если в большинстве случаев это бесполезно.

Изучить Clojure довольно легко для функционального языка, намного проще, чем Haskell (от которого я отказывался несколько раз), и кривая обучения довольно плавная. Если вы пытаетесь заняться функциональным программированием, я настоятельно рекомендую начать с Clojure. Тем не мение…

Я заметил некоторые проблемы с сообществом, которое он разделяет с C ++: отрицание проблем и обвинение пользователя. Это гораздо менее серьезно, но в некоторых областях почти абсолютное. Любое упоминание о том, что скобки являются проблемой, конечно, очевидно, но вскоре я натолкнулся на другую: макрос может вычислять только одну форму. Предположение, что это досадное ограничение, вызывает множество комментариев типа «вам это никогда не понадобится» и «вы делаете это неправильно», не сумев показать хорошие альтернативы, а во многих случаях просто полностью игнорируя приведенные мною примеры. Или худший ответ: «это даже не имеет смысла» - это ответ, который вы получаете от людей, которые настолько глубоко знают язык, что не могут выйти из рамки. Это очень жаль, и поэтому невозможно внести исправления для этих проблем. Вы не можете решить проблему, о существовании которой даже не можете согласиться.

Еще одна проблема заключалась в том, что все недостатки языка можно «исправить» с помощью макросов. Конечно, не может, но это аргумент. Но еще хуже, чем то, что этот аргумент в конечном итоге ложен, состоит в том, что когда он правдив, это не сила, а слабость.

Каждая функция и макрос - это новый язык

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

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

С другой стороны, в мире Лиспа все пользователи могут сами увеличивать размер языка с помощью макросов. Но это еще хуже! Фактически, все нетривиальные функции также являются расширениями языка. Я объясню как скоро.

Просто стало сложно

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

  1. Аргумент простоты: разные вещи должны быть разными. Иметь принципиально разные вещи, выглядящие очень похожими - это на самом деле риск и проблема, а не сила. Человеческий мозг легко справляется с особенностями английского языка, он может справиться с небольшими наборами правил нормальных языков программирования. Однако у него огромные проблемы с простотой, о чем свидетельствует постоянное сопротивление теории эволюции.
  2. Включение инструментов: если эта гипотеза верна, где инструменты? Должно быть так, чтобы каждый мог их написать, но почему-то есть больше и лучше инструментов (таких как инструменты рефакторинга, IDE), например, Python, язык, от которого сторонники Lisp приходят в ужас из-за таких вещей, как значительные пробелы и синтаксис с множеством специальных правил.

Я думаю, что отсутствие инструментов для Clojure во многом связано с системой определения сигнатур функций. У него есть несколько интересных свойств, первое - это то, что есть только позиционные аргументы. Чтобы обойти это ограничение, вы можете использовать различные уловки, например:

  1. Просто принять один аргумент, который является словарем / картой, и использовать деструктуризацию карты. Например: (foo {: params ‘(1 2),: else‘ (1, 2)})
  2. Перемежая ключевые слова («атомы» для людей с erlang, в основном специально типизированные строки) в список аргументов и создавая из этого структуру. Например: (foo 1 2: else 3 4)
  3. С макросами: вставка символов в список аргументов. Например: (foo 1 2 else 3 4)
  4. Применение структуры к плоскому списку аргументов по некоторому шаблону. Например: (foo 1 2 3 4)

(Пункт 1 отличается тем, что у него есть неприятный недостаток, заключающийся в том, что он извлекает только данные из словаря. Он не подтверждает, что он пуст в конце, что приводит к функциям, которые молча игнорируют неожиданные вводы, как в JavaScript.)

Общие проблемы со всеми этими решениями: а) Они разные. Итак, чтобы знать, какой именно этот сайт вызова вам нужен, вы должны знать их наизусть или просмотреть документацию, и б) все они реализованы процедурно внутри тела функции / макроса. Первый пункт болезнен и утомителен. Второй - абсолютный убийца. Такой анализ аргументов и делает инструменты в основе своей безнадежными. Полные определения по Тьюрингу сигнатур функций, которые даже не выводятся в какой-то промежуточный формат, который может обрабатывать инструментарий! Конечно, ребятам, создающим Clojure IDE, приходится нелегко. Они никогда не смогут обрабатывать написанный пользователем код или непонятные библиотеки.

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

Языковой дизайн - это сложно, не позволяйте всем это делать

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

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

Пусть тот, кто без греха, бросит первый камень

Конечно, я часть проблемы. Я, например, написал instar, классную библиотеку, которая была бы отличной частью стандартной библиотеки, но это еще один неприятный DSL для изучения в качестве библиотеки. Это локальное улучшение, которое в глобальном масштабе ухудшает положение.

Я даже разработал исправление отсутствия аргументов ключевых слов в Clojure под названием defk, которое представляет собой макрос, который вычисляет другой макрос и функцию (две формы с помощью магии изменчивости и процедурного кода: P).

Заключение

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