macros - usar - ¿Alguien me puede explicar el concepto de ''higiene''(soy programador de esquemas)?
visual basic en excel 2013 (6)
¡Me alegra saber que este lenguaje todavía se está utilizando! El código higiénico es un código que cuando se inyecta (a través de una macro) no causa conflictos con las variables existentes.
Hay mucha información buena en Wikipedia sobre esto: http://en.wikipedia.org/wiki/Hygienic_macro
Entonces ... soy nuevo en el esquema de r6rs, y estoy aprendiendo macros. ¿Alguien me puede explicar qué se entiende por "higiene"?
Gracias por adelantado.
Aparte de todas las cosas mencionadas, hay otra cosa importante para las macros higiénicas de Scheme, que se desprenden del ámbito léxico.
Digamos que tenemos:
(syntax-rules () ((_ a b) (+ a b)))
Como parte de una macro, seguramente insertará el +, también lo insertará cuando ya haya un +, pero luego otro símbolo que tenga el mismo significado que +
. Enlaza los símbolos con el valor que tenían en el entorno léxico en el que se encuentran las syntax-rules
, no en el lugar en que se aplican, estamos, después de todo, con un ámbito léxico. Lo más probable es que inserte un símbolo completamente nuevo allí, pero uno que esté vinculado globalmente al mismo significado que +
está en el lugar donde se define la macro. Esto es más útil cuando usamos una construcción como:
(let ((+ *))
; piece of code that is transformed
)
El escritor, o usuario de la macro, por lo tanto, no necesita ocuparse de asegurar que su uso vaya bien.
Esto es lo que encontré. ¡Explicar lo que significa es otra cuestión!
http://www.r6rs.org/final/html/r6rs-lib/r6rs-lib-Z-H-1.html#node_toc_node_sec_12.1
Las macros transforman el código: toman un bit de código y lo transforman en otra cosa. Como parte de esa transformación, pueden rodear ese código con más código. Si el código original hace referencia a una variable a
, y el código que se agrega a su alrededor define una nueva versión de a
, entonces el código original no funcionará como se esperaba porque accederá a la incorrecta a
: si
(myfunc a)
es el código original, que espera que un entero sea un entero, y la macro toma X
y la transforma en
(let ((a nil)) X)
Entonces la macro funcionará bien para
(myfunc b)
pero (myfunc a)
se transformará a
(let ((a nil)) (myfunc a))
lo que no funcionará porque myfunc
se aplicará a nil
lugar del entero que espera.
Una macro higiénica evita este problema de acceso a la variable incorrecta (y un problema similar a la inversa), asegurando que los nombres utilizados sean únicos.
Wikipedia tiene una buena explicación de http://en.wikipedia.org/wiki/Hygienic_macro .
Si imagina que una macro simplemente se expande en el lugar donde se usa, entonces también puede imaginar que si usa una variable a
en su macro, es posible que ya haya una variable definida en el lugar donde se usa esa macro.
¡Este no es el que quieres!
Un sistema macro en el que algo como esto no puede ocurrir, se llama higiénico .
Hay varias maneras de lidiar con este problema. Una forma es simplemente usar nombres de variables muy largos, muy crípticos e impredecibles en sus macros.
Una versión ligeramente más refinada de esto es el enfoque de gensym
usado por algunos otros sistemas de macros: en lugar de usted , el programador viene con un nombre de variable muy largo, muy críptico, muy impredecible, puede llamar a la función gensym
que genera un nombre muy largo Nombre de variable muy críptico, muy impredecible y único para usted .
Y como dije, en un sistema macro higiénico, tales colisiones no pueden ocurrir en primer lugar. Cómo hacer que un macro sistema sea higiénico es una pregunta interesante en sí misma, y la comunidad Scheme ha pasado varias décadas en esta pregunta, y siguen encontrando mejores y mejores formas de hacerlo.
La higiene se utiliza a menudo en el contexto de las macros. Una macro higiénica no usa nombres de variables que puedan arriesgarse a interferir con el código en expansión. Aquí hay un ejemplo. Digamos que queremos definir la forma or
especial con una macro. Intuitivamente,
(or abc ... d)
se expandiría a algo como (let ((tmp a)) (if tmp a (or bc ... d)))
. (Estoy omitiendo el caso vacío (or)
por simplicidad).
Ahora, si el nombre tmp
se agregó realmente en el código como en la expansión del bosquejo anterior, no sería higiénico y malo, ya que podría interferir con otra variable con el mismo nombre. Oye, queríamos evaluar
(let ((tmp 1)) (or #f tmp))
Usando nuestra expansión intuitiva, esto se convertiría en
(let ((tmp 1)) (let ((tmp #f)) (if tmp (or tmp)))
El tmp
de la macro sombrea el tmp
más externo, por lo que el resultado es #f
lugar de 1
.
Ahora, si la macro era higiénica (y en Esquema, es automáticamente el caso cuando se usan syntax-rules
), en lugar de usar el nombre tmp
para la expansión, utilizaría un símbolo que se garantiza que no aparecerá en ninguna otra parte del código. . Puedes usar gensym
en Common Lisp.
On Lisp de Paul Graham tiene material avanzado sobre macros.