Пора всё же написать про лисп несколько гадостей (уже наболело).
1. По де-факто стандарту определение пространства имён и реализация этого пр-ва имён - это два разных файла. Т.е., как в С, а не как в Паскале. Это неудобно и вообще прошлый век.
Нет операции (with-namespace &body body) - может быть, её и можно сделать, но по-нормальному её можно сделать только через модификацию readtable. Но если две подсистемы меняют readtable и (не дай Бог) поменяют один и тот же символ, то между ними будет проблема и конфликт. Поэтому, для низкоуровневых вещей, которые потом предполагается включить в свой постоянный инструментарий, readtable менять нежелательно. А если импортировать множество разных мелких пр-в имён, то неизбежны конфликты при импорте. То есть, среда не поощряет создания множества мелких пространств имён, а поощряет создание небольшого числа крупных. А это плохо (жду конструктивных возражений).
2. Вообще, убогие пространства имён. Они не могут быть вложенными, как a::b::c. Только "плоские" пространства имён a::b В обычных языках, как правило, класс/структура задают пр-во имён. Собственно, если и есть в ООП какая-то сила, так это в том, что не нужно писать instance.field, а можно просто написать field, если находишься внутри метода. А вовсе не в том, что думают об этом апологеты ООП. В лиспе так написать нельзя (хотя есть конструкция with-slots, но в маленьких функциях она не помогает сократить код).
3. многословность. Сложение двух строк по стандарту (concatenate 'string "as" "df") а также в стандарте некоторым примитивным операциям присвоены имена, подобные destructuring-bind или multiple-value-bind
4. Вместо мойЛюбимыйИдентификатор нужно писать мой-любимый-идентификатор. Вроде мелочь, а места на экране жрётся море. Конечно, никто не заставляет, но есть проблемы с введением идентификаторов, чувствительных к регистру, поскольку в стандарте по умолчанию всё приводится к верхнему регистру и все стандартные символы - в верхнем регистре. То, есть, (print 'мойЛюбимыйИдентификатор) = МОЙЛЮБИМЫЙИДЕНТИФИКАТОР
5. Отсюда - следствие: В обычных языках пишем instance1.field1.field2 В лиспе (в лучшем случае) - (field2-of (field1-of instance1)) 4 лишних символа на каждую ссылку, не считая пробела, который тоже жрёт место на экране. Если брать голый, ничем не подслащённый стандарт, то может оказаться и (slot-value (slot-value instance1 'field1) 'field2)
6. Дебаггер. Вряд ли получится посмотреть переменные в том виде, как они определены в исходнике. Компилятор слишком умный и их соптимизирует. Вряд ли получится поставить брекпойнт на точке в исходнике, по той же причине. Отлаживаться нужно через логи и трейсы. Впрочем, динамичный характер языка делает такую отладку лёгкой.
7. Не хватает множества идиом. Например, есть push (деструктивно добавить элемент в начало списка). Но нет nconcf (добавить к переменной, содержащей список, другой список). Поскольку эти идиомы реально нужны, каждый вводит их по-своему и единый язык распадается на множество диалектов. То же сложение строк никто не делает как (concatenate 'string ...) Я ещё не вполне в культуре, но я не думаю, что есть де-факто стандарт на сокращённую запись этого выражения.
8. CLOS. Да, CLOS велик и могуч, в нём есть мультиметоды. Но в нём есть и такая фишка, как "изменить класс в рантайме". Это круто, но это - накладные расходы.
9. Отсутствует декларативная статическая типизация. Да-да, она самая. На самом деле, информация, содержащаяся в определении типов, очень помогает писать более лаконичный код. На самом деле, возможность (необязательной) статической типизации в лиспе есть, но нет возможности использовать её для сокращения кода.
Конечно, да. Лисп велик и могуч и на нём можно делать то, что нельзя больше делать ни на чём другом. У него есть такие степени свободы, которые продвинутый си-плюс-плюсник просто никогда в жизни не сможет осознать. Но... Обычный язык общего назначения в лиспе нужно тоже делать DSL-ем. В том числе, нужен и DSL для полноценной работы со списками (а уж это - основа лиспа). DSL для удобного определения CLOS-классов. Каждый пишет такой DSL, но нет стандарта де-факто. Я об этом уже писал.
Последнее время я работаю с лиспом более плотно, чем раньше. Я уже начинаю сомневаться в целесообразности темы "лисп как препроцессор для С". Есть и практические к тому иллюстрации. Лисп сообщество не смогло по сей день решить некоторые тривиальные задачи:
1. asdf. Не умеет делать clean (т.е., нет стандарта на эту операцию). Не умеет пересобирать часть систем. Например, если я говорю recompile :force t своей системе, у меня пересобирается вся лисповая часть sbcl. И это - только одна из недоделок asdf. Т.е., при всей крутости лиспа, де-факто стандартное средство сборки гораздо слабее, чем make.
2. средства автодокументирования. Несмотря на то, что в лиспе делать автодокументацию на порядок удобнее, чем в каком-нибудь там С++, я не нашёл средства документированиия, подобного doxygen по мощности. Только не надо меня лечить, что есть describe и т.п. Документация удобна, когда она существует в виде книжечки. Один раз прочитал - и уже знаешь, куда тыркаться. Интерактивная помощь нужна, если что-то забыл, разбираться по ней не всегда удобно (хотя apropos рулит, конечно).
Лисперы, ваши комменты приветствуются. Особенно, в части решения означенных проблем.
Я не ставлю себе цель обосрать лисп. На самом деле, я всё равно хочу писать на лиспе. Просто я давно пытаюсь понять - если он так крут, то почему же он уступил своё первое место мейнстрим-языка высокого уровня какой-то там Яве? Мне кажется, что я привёл здесь причины, основная из которых, если обобщать - лисп неудобен для простых вещей, в которых не нужны макросы. А этих простых вещей всё же больше. Видимо, при разработке следовало сделать более лёгким основной синтаксис. Создание макросов затруднилось бы, но макросов в коде обычно не больше 10-20%, так что общая трудоёмкость могла бы снизиться, а удобочитаемость заведомо повысилась бы.
И, конечно, интересно найти выход из этого положения.