Одна из вещей, за которые я не люблю Common Lisp, — это то, что в нём ни на что нельзя рассчитывать. Например, в более функциональных Лиспах большинство функций недеструктивны и не имеют побочных эффектов. В CL пока не прочитаешь CLHS лучше функцию не вызывать. И это хорошо, если функция стандартная и вы — конечный пользователь. CLOS добавляет свою порцию неопределённости. Например, декораторы методов (:before, :after) сами по себе требуют знания особенностей декорируемой функции. Должен ли я вызывать свой метод до или после оригинального? А как оригинальный метод поменяет состояние объекта? А не нарушу ли я чего-то? Без каких-то гарантий иногда бывает очень сложно спроектировать хорошую библиотеку.
К слову, в Clojure мултиметоды организованы гораздо проще, тем не менее свою главную функцию (диспетчеризацию по произвольному признаку, будь то классы параметров, значения или положение звёзд на небе) они выполняют.
Некоторый код зачастую проще и/или эффективнее написать в виде цикла с мутацией, в императивном стиле. Например, недавно пришлось решить задачу генерации непосредственно сочетаний из n по k, и рекурсивный алгоритм на диалекте Scheme — Racket — для генерации 2118760 (n = 50, k = 4) комбинаций потратил около 2.4 секунды. Подозреваю, что итеративный вариант алгоритма будет работать на порядок-два быстрее (сегодня-завтра проверю).
P.S. Привести рекурсивный вариант к tail-recursive не получается, так как комбинации ещё и обрабатывать нужно, вызывая пользовательскую функцию.
Кстати, да, в CL принято ставить в начало имени букву 'n': nreverse, например. Да и вообще, по сравнению с Racket в CL в плане именования функций — разброд и шатание. Тяжёлое наследие прошлого, наверное.
>Должен ли я вызывать свой метод до или после оригинального? А как оригинальный метод поменяет состояние объекта? А не нарушу ли я чего-то?
Эта проблема присуща любому ОО-подходу: когда переопределяете/доопределяете функциональность, вы должны знать, как это отразится на состоянии объекта (если объекты мутабельны), на контрактах.
>Например, в более функциональных Лиспах большинство функций недеструктивны и не имеют побочных эффектов.
Да, CL достаточно далек от чистой функциональщины.
> Эта проблема присуща любому ОО-подходу: когда переопределяете/доопределяете функциональность, вы должны знать, как это отразится на состоянии объекта (если объекты мутабельны), на контрактах.
В «традиционных» ОО-языках нормой является помечать классы и методы, которые не желательно изменять, как final, а поля как private/protected. Например, в Google Collections (которые сейчас являются частью Guava) большинство коллекций являются финитными, поэтому нарушить там что-то довольно сложно. Идеалогия же Лиспа подразумевает возможность изменить что угодно и как угодно. Не то, чтобы это плохо, но возможностей отстрелить себе ногу гораздо больше.
Обобщенные функции CLOS