Может ли кто-нибудь объяснить мне понятие "гигиена" (я программист)?
Итак... Я новичок в схеме r6rs и изучаю макросы. Может кто-нибудь объяснить мне, что означает "гигиена"?
Заранее спасибо.
6 ответов
Гигиена часто используется в контексте макросов. Гигиенический макрос не использует имена переменных, которые могут помешать расширению кода. Вот пример. Допустим, мы хотим определить or
специальная форма с макросом. Наглядно,
(or a b c ... d)
будет расширяться до чего-то вроде (let ((tmp a)) (if tmp a (or b c ... d)))
, (Я опускаю пустое (or)
случай для простоты.)
Теперь, если имя tmp
фактически был добавлен в код, как в приведенном выше наброске, это было бы не гигиенично, а плохо, потому что это могло бы помешать другой переменной с тем же именем. Скажем, мы хотели оценить
(let ((tmp 1)) (or #f tmp))
Используя наше интуитивное расширение, это станет
(let ((tmp 1)) (let ((tmp #f)) (if tmp (or tmp)))
tmp
из макро-теней самый внешний tmp
и так результат #f
вместо 1
,
Теперь, если макрос был гигиеническим (и в схеме, это автоматически происходит при использовании syntax-rules
), то вместо использования имени tmp
для расширения вы должны использовать символ, который гарантированно не появится где-либо еще в коде. Ты можешь использовать gensym
в Common Lisp.
В "On Lisp" Пола Грэма есть продвинутый материал по макросам.
Если вы представляете, что макрос просто раскрывается в место, где он используется, то вы также можете представить, что если вы используете переменную a
в вашем макросе уже может быть переменная a
определяется в месте, где используется этот макрос.
Это не a
что ты хочешь!
Макросистема, в которой нечто подобное не может произойти, называется гигиенической.
Есть несколько способов справиться с этой проблемой. Один из способов - просто использовать в своих макросах очень длинные, очень загадочные, очень непредсказуемые имена переменных.
Немного более изысканная версия этого gensym
подход, используемый некоторыми другими макрос-системами: вместо вас, программиста, который придумал очень длинное, очень загадочное, очень непредсказуемое имя переменной, вы можете вызвать gensym
функция, которая генерирует очень длинное, очень загадочное, очень непредсказуемое и уникальное имя переменной для вас.
И, как я сказал, в гигиенической макросистеме такие столкновения не могут происходить в первую очередь. Как сделать гигиеническую систему макросов - интересный вопрос сам по себе, и сообщество Scheme потратило несколько десятилетий на этот вопрос, и они продолжают придумывать лучшие и лучшие способы сделать это.
Я так рад узнать, что этот язык все еще используется! Гигиенический код - это код, который при введении (через макрос) не вызывает конфликтов с существующими переменными.
В Википедии есть много полезной информации об этом: http://en.wikipedia.org/wiki/Hygienic_macro
Макросы преобразуют код: они берут один бит кода и превращают его во что-то другое. В рамках этого преобразования они могут окружить этот код большим количеством кода. Если исходный код ссылается на переменную a
и код, который добавлен вокруг него, определяет новую версию a
, тогда исходный код не будет работать должным образом, потому что он будет обращаться к неправильному a
: если
(myfunc a)
это оригинальный код, который ожидает a
быть целым числом, а макрос принимает X
и превращает его в
(let ((a nil)) X)
Тогда макрос будет нормально работать для
(myfunc b)
но (myfunc a)
будет преобразован в
(let ((a nil)) (myfunc a))
который не будет работать, потому что myfunc
будет применяться к nil
а не целое число, которое он ожидает.
Гигиенический макрос позволяет избежать этой проблемы, связанной с обращением к неверной переменной (и аналогичной проблемы, наоборот), обеспечивая уникальность используемых имен.
В Википедии есть хорошее объяснение гигиенических макросов.
Помимо всего перечисленного, в гигиенических макросах Схемы есть еще одна важная вещь, вытекающая из лексического контекста.
Скажем, у нас есть:
(syntax-rules () ((_ a b) (+ a b)))
Как часть макроса, он обязательно вставит +, он также вставит его, когда уже есть +, но затем другой символ, который имеет то же значение, что и +
, Он связывает символы со значением, которое они имели в лексической среде, в которой syntax-rules
ложь, а не там, где она применяется, в конце концов, мы ограничены лексической областью. Скорее всего, он вставит туда совершенно новый символ, но глобально связанный с тем же значением, что и +
находится в месте, где определен макрос. Это наиболее удобно, когда мы используем такую конструкцию, как:
(let ((+ *))
; piece of code that is transformed
)
Автор или пользователь макроса, таким образом, не должны быть заняты обеспечением его использования.
Вот что я нашел. Объяснение того, что это значит, совсем другое дело!