LINUX.ORG.RU

Рефакторинг лиспа средствами самого лиспа


0

1

Всем привет!

Неоднократно приходилось слышать такое утверждение: в лиспе возможности рефакторинга, профилирования, отладки и многого другого достигаются средствами самого языка. Т.е. без использования вспомогательных средств, нередко усложнённых, как например в Eclipse или IDEA для Явы. В принципе, я готов в это поверить, т.к. язык очень могучий, и всё, что я до сих пор освоил в нём это лишь верхушка айсберга. Поэтому хочу попросить местных гуру-лисперов продемонстрировать вышесказанное на двух простеньких примерах,

1. Рефакторинг «Смена сигнатуры». Пусть есть функция, которая вызывается в сотне мест. Понадобилось а) переименовать функцию, б) добавить параметр. Хочеться, чтобы вызовы функции поменялись на новые во всех местах, где она используется. В место недостающего параметра подставлять NIL, ну или какие-то конкретные значения в зависимости от контекста.
2. Рефакторинг «Подъём метода». Пусть есть класс А, и наследующий от него класс В (классы понимаются в терминах CLOS, разумееться). Хочется переместить некий метод из класса В в класс-родитель А, или наоборот, это будет уже «спуск метода».

ХОтелось бы рассмотреть возможность подобного рефакторинга как в случае работы с образом (например, подключения к работающией системе через REPL), так и в случае с деревом исходных текстов в файловой системе.

Спасибо заранее!

ЗЫ. Под «лсипом» понимается Common Lisp, конкретно - SBCL 1.0.37 под линуксом федорой.

Ignatik
() автор топика
Ответ на: комментарий от anonymous

Спасибо за ссылку, хотя я так понял у него там ничего готового нету. Только язык для определения паттернов. В принципе да, я чего-то такого и ожидал, DSL на лиспе для задания правил рефакторинга

Ignatik
() автор топика
Ответ на: комментарий от anonymous

И не очень понятно, оно у него работает на образах, или на дереве исходников тоже

Ignatik
() автор топика

> 1. Рефакторинг «Смена сигнатуры». Пусть есть функция, которая вызывается в сотне мест. Понадобилось а) переименовать функцию, б) добавить параметр. Хочеться, чтобы вызовы функции поменялись на новые во всех местах, где она используется. В место недостающего параметра подставлять NIL, ну или какие-то конкретные значения в зависимости от контекста.

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

anonymous
()

2. Вы неправильно говорите и мыслите. В CLOS методы не принадлежат классам, а существуют параллельно с классами. Соответственно, то, что вы хотите, на самом деле есть не «перемещение метода», а «смена спецификатора метода». Ну тут все просто - в форме defmethod меняем спецификатор. Т.е., было [code=lisp] (defmethod foo ((x class-b) param-1 param-2) ...) [/code] а стало [code=lisp] (defmethod foo ((x class-a) param-1 param-2) ...) [/code] 1. Достигается за счет средств поиска определений. Например, swank:find-definition-for-thing. Но обычно сам рефакторинг не делается в самом лиспе, потому что удобнее исходники редактировать в редакторах, а не из самого лиспа.

dmitry_vk ★★★
()

Смена сигнатуры средствами лиспа : добавление

&optional
параметра, автоматически будет подставляться nil у всех методов которые данный параметр не переопределеят. Ну чем не рефакторинг?

anonymous
()
Ответ на: комментарий от anonymous

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

А если функция не переопределяется и не вызывается из eval? //lisp не знаю

Dimanc ★★
()
Ответ на: комментарий от Dimanc

>А если функция не переопределяется и не вызывается из eval?

Просто так взять и перекомпилировать функцию не получится - в лисп-образе обычно не хранится исходный код функции, но обычно есть ссылка на то место, где функция была определена (в виде пары имя файла + положение в файле). Соответственно, можно поменять этот файл, скомпилировать и загрузить его.

dmitry_vk ★★★
()

> в лиспе возможности рефакторинга

Вменяемых инструментов рефакторинга для Common Lisp нет и сама возможность их создания представляется сомнительной.

archimag ★★★
()
Ответ на: комментарий от Dimanc

> А если функция не переопределяется и не вызывается из eval?

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

anonymous
()
Ответ на: комментарий от archimag

>сама возможность их создания представляется сомнительной.

Я бы не согласился с этим утверждением.

dmitry_vk ★★★
()

Переименовать ф-ю в исходниках голыми руками нельзя, хотя лисп кое в чём может помочь, например, найти некоторые (не обязательно все) точки вызова этой ф-ии в живом образе. Нужно работать текстовым редактором: поиск по файлам, поиск с заменой.

Для добавления аргумента во многих случаях можно добавить параметр-ключ, например,
(defun foo (x) ...) ; старое определение
(defun foo (x &key new-arg) ... ) ; новое

Изменения в точках вызова тут просто не нужны. Как правило, я не пользуюсь функциями более чем с тремя-четырьмя постоянными параметрами, всё, что сверх того - параметры-ключи.

Насчёт CLOS вроде сказали другие.

Работать с деревом исходников в файловой системе не получится по природе лиспа, т.к. его можно в любой точке расширить и смысл языка поменяется. Расширяемость даёт гибкость, но за всё надо платить.

С другой стороны, в лиспе есть средства поиска определений. Если нужно заниматься чем-то типа рефакторинга, нужно загрузить систему и уже в «живом» состоянии пользоваться поиском определений. Из-за расширяемости языка вряд ли получится сделать рефакторинг столь же гладким и универсальным, как в Java.

den73 ★★★★★
()
Ответ на: комментарий от den73

>Из-за расширяемости языка вряд ли получится сделать рефакторинг столь же гладким и универсальным, как в Java.

В java он тоже не такой уж гладкий за счет того, что там мода на xml-конфиги, в которых сложно универсальным образом делать замену.

dmitry_vk ★★★
()
Ответ на: комментарий от anonymous

>> 1. Рефакторинг «Смена сигнатуры». Пусть есть функция, которая вызывается в сотне мест. Понадобилось а) переименовать функцию, б) добавить параметр. Хочеться, чтобы вызовы функции поменялись на новые во всех местах, где она используется. В место недостающего параметра подставлять NIL, ну или какие-то конкретные значения в зависимости от контекста.

Это сделать невозможно


как варианты
1. вместо defun сделать свой макрос, который будет заполнять что-то вроде символьной таблицы, и другой макрос-вызов функции. Тогда рефакторить нужно будет только такие макросы.
2. рефакторинг может не понадобиться: в лиспе есть параметры с ключевыми словами - функции прозрачно расширяются добавлением новых ключ. параметров.
3. подвариант 2 с обобщёнными методами и реализациями-специализациями. Не надо переписывать call sites при добавлении новой реализации.

anonymous
()
Ответ на: комментарий от dmitry_vk

> В java он тоже не такой уж гладкий за счет того, что там мода на xml-конфиги, в которых сложно универсальным образом делать замену.
Это - хорошо :-)

den73 ★★★★★
()
Ответ на: комментарий от den73

> например, найти некоторые (не обязательно все) точки вызова этой ф-ии в живом образе

Вот! Это то что надо. Не подскажете как это делается, в какую сторону копать?

Ignatik
() автор топика
Ответ на: комментарий от Ignatik

> Вот! Это то что надо. Не подскажете как это делается, в какую сторону копать?
http://common-lisp.net/~sboukarev/slime/Cross_002dreference.html
и далее лезем в исходники SLIME и смотрим, как сделано.

Но этого недостаточно. Если функция вызывается через apply или eval, то эти вызовы могут быть не найдены. Также могут быть не найдены вызовы при высоких показателях оптимизации и inline (не пробовал такие искать никогда, да и наверняка это зависит от реализации).

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

den73 ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.