Pull to refresh

Comments 9

Одна из вещей, за которые я не люблю Common Lisp, — это то, что в нём ни на что нельзя рассчитывать. Например, в более функциональных Лиспах большинство функций недеструктивны и не имеют побочных эффектов. В CL пока не прочитаешь CLHS лучше функцию не вызывать. И это хорошо, если функция стандартная и вы — конечный пользователь. CLOS добавляет свою порцию неопределённости. Например, декораторы методов (:before, :after) сами по себе требуют знания особенностей декорируемой функции. Должен ли я вызывать свой метод до или после оригинального? А как оригинальный метод поменяет состояние объекта? А не нарушу ли я чего-то? Без каких-то гарантий иногда бывает очень сложно спроектировать хорошую библиотеку.

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

(Хотя за статью, конечно, спасибо)
есть вполне опреленные соглашение о том как писать мутаторы (восклицательный знак в названии)

Но я просто не советую пользоваться мутацией в лиспе. Ведь ничего не мешает сделать что-то и без изменения данных(см Фунциональное программирование).

Некоторый код зачастую проще и/или эффективнее написать в виде цикла с мутацией, в императивном стиле. Например, недавно пришлось решить задачу генерации непосредственно сочетаний из n по k, и рекурсивный алгоритм на диалекте Scheme — Racket — для генерации 2118760 (n = 50, k = 4) комбинаций потратил около 2.4 секунды. Подозреваю, что итеративный вариант алгоритма будет работать на порядок-два быстрее (сегодня-завтра проверю).

P.S. Привести рекурсивный вариант к tail-recursive не получается, так как комбинации ещё и обрабатывать нужно, вызывая пользовательскую функцию.
> есть вполне опреленные соглашение о том как писать мутаторы (восклицательный знак в названии)

В том то и дело, что конкретно в Common Lisp такая традиция не прижилась (напр. remove-if-not vs. delete-if-not, второй вариант — мутабельный).
Кстати, да, в CL принято ставить в начало имени букву 'n': nreverse, например. Да и вообще, по сравнению с Racket в CL в плане именования функций — разброд и шатание. Тяжёлое наследие прошлого, наверное.
>Должен ли я вызывать свой метод до или после оригинального? А как оригинальный метод поменяет состояние объекта? А не нарушу ли я чего-то?

Эта проблема присуща любому ОО-подходу: когда переопределяете/доопределяете функциональность, вы должны знать, как это отразится на состоянии объекта (если объекты мутабельны), на контрактах.

>Например, в более функциональных Лиспах большинство функций недеструктивны и не имеют побочных эффектов.

Да, CL достаточно далек от чистой функциональщины.
> Эта проблема присуща любому ОО-подходу: когда переопределяете/доопределяете функциональность, вы должны знать, как это отразится на состоянии объекта (если объекты мутабельны), на контрактах.

В «традиционных» ОО-языках нормой является помечать классы и методы, которые не желательно изменять, как final, а поля как private/protected. Например, в Google Collections (которые сейчас являются частью Guava) большинство коллекций являются финитными, поэтому нарушить там что-то довольно сложно. Идеалогия же Лиспа подразумевает возможность изменить что угодно и как угодно. Не то, чтобы это плохо, но возможностей отстрелить себе ногу гораздо больше.
Пример с new меня смущает. Ведь есть же стандартный метод initialize-instance, в котором надо делать инициализацию объекта.
Ну скорее уж shared-initialize.
А вообще, ну да, это просто пример, высосанный из пальца.
Sign up to leave a comment.

Articles