← Blog of oldlisper

Безымянный враг

· 29.04.2011 00:00
· original author: archimag

Безымянный враг

При программировании на Common Lisp довольно часто приходиться заниматься отладкой. И (вопреки мнению сторонников статической типизации) в этом нет какой-либо проблемы, наоборот, отладчик это лучший друг и советник, позволяющий лучше понять как на самом деле работает программа и какие нюансы не были учтены. Да весь процесс разработки на CL основан именно на постоянном взаимодействии с отладчиком.

Но, порой случается неприятная проблема - вдруг отладчик показывает вам стэк вызовов, в верхней части которого расположенною несколько безымянных лямбд, а сразу после них идут какие-то системные вызовы, не имеющие никакого отношения к вашему коду, так что понять что либо на основе такого стэка вызовов решительно невозможно. Приходится брать в руки различные музыкальные инструменты, расставлять диагностические сообщения и вчитываться в код. Обычно проблема оказывается в какой-то ерунде, на которую компилятор не смог обратить ваше внимание (а он что, он сам не знал) и исправление тривиально, а вот поиск места ошибки может потребовать времени. Столкнувшись с подобные некоторые могут начать потирать руки "А я вас предупреждал!", другие жаловаться на сложности пошаговой отладки в SLIME и тыкать пальцем в коммерческие реализации (может ли хорошая работа быть сделана бесплатно?). Но я знаю один простой рецепт, который никогда не позволит проблеме скрыться в безымянных джунглях:

НИКОГДА не используйте безымянные лямбда

Всё просто, если каждая строчка в стэк вызовов будет иметь своё уникальное имя, то почти всегда вы сможете с лёгкостью диагностировать место ошибки. Как наделить лямбду именем? В библиотеке alexandria специально для этого есть простой и удобный макрос named-lambda. Если ваш проект не использует alexandria или вы не хотите подключать её из-за одного единственного макроса (хотя там внутри много разных полезных штук), то можно тривиально определить данный макрос в своём коде:

(defmacro named-lambda (name lambda-list &body body)
  `(labels ((,name ,lambda-list ,@body))
     #',name
)
)

Приятной отладки!