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

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

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


person Cam    schedule 09.06.2010    source источник
comment
Ну, знаешь, купаться, чистить зубы, выковыривать еду из бороды. Вещи, с которыми у большинства программистов возникают проблемы.   -  person JSBձոգչ    schedule 10.06.2010
comment
@JS: Я сижу здесь уже несколько дней и ни по какой причине не отхожу от своего компьютера. Вот как сильно я пытаюсь научиться гигиене, но я все еще не могу понять концепцию. На самом деле, чем дольше я сижу здесь, тем хуже становится :(   -  person Cam    schedule 10.06.2010
comment
Мне любопытно, для чего вы используете Scheme?   -  person sholsapp    schedule 10.06.2010
comment
Предлагаю хорошенько выспаться. Это творит чудеса с пониманием. Я никогда не был таким продуктивным и ясным, как когда у меня есть свои 8,5 часов в сутки. :)   -  person Paul Nathan    schedule 10.06.2010
comment
@gnucom: Для развлечения. Также я слышал (и вижу), что его можно быстро использовать для прототипирования алгоритмов/идей, поэтому, как только я освоюсь с ним, я буду использовать его для этого.   -  person Cam    schedule 10.06.2010
comment
@incrediman: Хех, это забавно, это именно то, для чего я его использую (весело).   -  person sholsapp    schedule 10.06.2010
comment
Я не уверен, что вы имеете в виду в своем заголовке: программисты Scheme обычно более гигиеничны, чем, скажем, программисты C. Они определенно более гигиеничны, чем большинство программистов Perl, которых я встречал. Я думаю, вы имеете в виду, что как программист Scheme вы просто никогда не думаете об этом.   -  person Joel J. Adamson    schedule 03.08.2010


Ответы (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.

Пол Грэм о Лиспе содержит дополнительные материалы по макросам.

person namin    schedule 09.06.2010

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

Это не то a, которое вам нужно!

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

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

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

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

person Jörg W Mittag    schedule 09.06.2010
comment
машет рукой Это не те a, которые вам нужны. - person Thanatos; 10.06.2010
comment
Это имеет несколько неправильных частей: (a) использование длинных загадочных имен в макросах не является решением, это всего лишь способ отсрочить макрос, в частности, он может привести к вопиющим сбоям, когда макрос используется внутри себя. (б) вы упоминаете только одну сторону проблемы, которую решает gensyms; другая сторона заключается в том, что привязки в определении макроса не могут быть затенены привязками при его использовании, например: (let ((if "bleh")) (my-macro)). Это не может быть решено только с помощью gensyms. - person Eli Barzilay; 10.06.2010

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

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

person sholsapp    schedule 09.06.2010

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

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

person JYelton    schedule 09.06.2010
comment
Это довольно просто, если вы знаете жаргон! Это означает, что гигиенические макросы не загрязняют таблицу символов. - person Paul Nathan; 10.06.2010
comment
Это очень неясный и корявый жаргон, и мне потребовалось, по сути, пройти курс для выпускников, чтобы понять, что происходит (я думаю, что другие люди умнее). - person Paul Nathan; 10.06.2010

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

(myfunc a)

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

(let ((a nil)) X)

Тогда макрос будет работать нормально для

(myfunc b)

но (myfunc a) превратится в

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

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

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

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

person psmears    schedule 09.06.2010

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

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

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

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

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

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

person Zorf    schedule 10.06.2010
comment
+1, это интересно; таким образом, он следует ожидаемому поведению как для автора макроса, так и для пользователя. - person Cam; 11.06.2010
comment
Да, гигиена — это просто способ обеспечить правильность, независимо от того, какие символы использовал автор или пользователь. Я действительно долго ломал голову, пытаясь понять логику того, почему (let ((if +)) ...), например, не потерпел крах, пока не понял, что это было из-за лексической области видимости. - person Zorf; 11.06.2010