Подтвердить что ты не робот

Может ли кто-то объяснить мне понятие "гигиены" (я программист схемы)?

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

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

4b9b3361

Ответ 1

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

Теперь, если макрос был гигиеничным (а в Scheme он автоматически используется при использовании syntax-rules), то вместо того, чтобы использовать имя tmp для расширения, вы должны использовать символ, который гарантированно не появится где-нибудь еще в коде. Вы можете использовать gensym в Common Lisp.

Paul Graham В Lisp есть расширенный материал по макросам.

Ответ 2

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

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

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

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

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

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

Ответ 3

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

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

Ответ 5

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

(myfunc a)

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

(let ((a nil)) X)

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

(myfunc b)

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

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

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

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

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

Ответ 6

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

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

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

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

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

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