Может ли кто-нибудь объяснить мне понятие «гигиена» (я программист)? - PullRequest
26 голосов
/ 10 июня 2010

Итак ... Я новичок в схеме r6rs и изучаю макросы.Может кто-нибудь объяснить мне, что означает «гигиена»?

Заранее спасибо.

Ответы [ 6 ]

22 голосов
/ 10 июня 2010

Гигиена часто используется в контексте макросов.Гигиенический макрос не использует имена переменных, которые могут помешать расширению кода.Вот пример.Допустим, мы хотим определить специальную форму 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 имеется расширенный материал по макросам.

7 голосов
/ 10 июня 2010

Если вы представляете, что макрос просто раскрывается в то место, где он используется, то вы также можете представить, что если вы используете переменную a в своем макросе, возможно, уже будет переменной a определено в месте, где используется этот макрос.

Это , а не a, который вы хотите!

Макросистема, в которой нечто подобное не может произойти, называется гигиеническая .

Существует несколько способов решения этой проблемы. Один из способов - просто использовать в своих макросах очень длинные, очень загадочные, очень непредсказуемые имена переменных.

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

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

3 голосов
/ 10 июня 2010

Я так рад узнать, что этот язык все еще используется! Гигиенический код - это код, который при введении (через макрос) не вызывает конфликтов с существующими переменными.

В Википедии есть много полезной информации об этом: http://en.wikipedia.org/wiki/Hygienic_macro

2 голосов
/ 11 июня 2010

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

Скажем, у нас есть:

(syntax-rules () ((_ a b) (+ a b)))

Как частьмакрос, конечно, он вставит +, он также вставит его, когда уже есть +, но затем другой символ, который имеет то же значение, что и +.Он связывает символы со значением, которое они имели в лексической среде, в которой лежит syntax-rules, а не там, где она применяется, в конце концов, мы ограничены лексической областью.Скорее всего, он вставит туда совершенно новый символ, но тот, который глобально связан с тем же значением, что и +, находится в том месте, где определен макрос.Это наиболее удобно, когда мы используем такую ​​конструкцию, как:

(let ((+ *))
  ; piece of code that is transformed
)

Автор или пользователь макроса, таким образом, не должны быть заняты обеспечением его использования.

2 голосов
/ 10 июня 2010

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

(myfunc a)

- исходный код, который ожидает, что a будет целым числом, а макрос принимает X и преобразует его в

(let ((a nil)) X)

. Затем макрос будет работать нормально для

(myfunc b)

, но (myfunc a) преобразуется в

(let ((a nil)) (myfunc a))

, который не будет работать, поскольку myfunc будет применяться к nil, а не к целому числу, которое он ожидает.

Гигиенический макрос позволяет избежать этой проблемы, связанной с обращением к неправильной переменной (и аналогичной проблемой наоборот), обеспечивая уникальность используемых имен.

Википедия имеет хорошее объяснение гигиенические макросы .

2 голосов
/ 10 июня 2010

Вот что я нашел. Объяснение того, что это значит, совсем другое дело!

http://www.r6rs.org/final/html/r6rs-lib/r6rs-lib-Z-H-1.html#node_toc_node_sec_12.1

...