Pregunta

Muchos ejemplos de macros parecen ser sobre ocultar lambdas, p. con archivo abierto en CL. Estoy buscando algunos usos más exóticos de macros, particularmente en PLT Scheme. Me gustaría tener una idea de cuándo considerar el uso de una macro versus el uso de funciones.

¿Fue útil?

Solución

Solo uso macros Scheme (define-syntax) para cosas pequeñas como una mejor sintaxis lambda:

(define-syntax [: x]
  (syntax-case x ()
    ([src-: e es ...]
     (syntax-case (datum->syntax-object #'src-: '_) ()
       (_ #'(lambda (_) (e es ...)))))))

Que te permite escribir

[: / _ 2]  ; <-- much better than (lambda (x) (/ x 2))

Dan Friedman tiene una implementación alucinante de OO utilizando macros: http: // www.cs.indiana.edu/~dfried/ooo.pdf

Pero honestamente, todas las macros útiles que he definido son robadas de Paul Graham's On Lisp y generalmente son más fáciles de escribir con defmacro (define-macro en el esquema PLT). Por ejemplo, aif es bastante feo con syntax-rules.

(define-syntax (aif x)
  (syntax-case x ()
    [(src-aif test then else)
     (syntax-case (datum->syntax-object (syntax src-aif) '_) ()
       [_ (syntax (let ([_ test]) (if (and _ (not (null? _))) then else)))])]))

syntax-case es extraño ya que solo es fácil de usar para macros muy simples, donde está contento de la incapacidad de capturar variables; y macro DSLs muy complicadas, donde te alegra la incapacidad de capturar variables fácilmente . En el primer caso, desea escribir el código sin pensarlo, y en el segundo caso, ha pensado lo suficiente sobre el DSL que está dispuesto a escribir parte de él en el lenguaje <=> / <=> que no es Scheme para evitar errores desconcertantes.


Pero no uso muchas macros en Scheme. El esquema idiomático es tan funcional que muchas veces solo desea escribir un programa funcional y luego ocultar algunas lambdas. Subí al tren funcional y ahora creo que si tienes un lenguaje vago o una buena sintaxis para lambda, incluso eso no es necesario, por lo que las macros no son tan útiles en un estilo puramente funcional.

Así que recomendaría Practical Common Lisp y En Lisp . Si desea utilizar el esquema PLT, creo que la mayoría de sus macros <=> funcionarán con <=>. O simplemente use Common Lisp.

Otros consejos

Se necesitan macros para implementar nuevas estructuras de control y nuevas construcciones de enlace.

Por lo tanto, busque este tipo de construcciones en http://planet.plt-scheme.org . En PLaneT, ambos navegan por la documentación y el código.

Ejemplos de nuevas estructuras de control:

http://planet.plt-scheme.org/package-source/soegaard/control.plt/2/0/planet-docs/manual/index.html

Para encontrar ejemplos de nuevas formas de enlace, busque macros que comiencen con " with - " ;. Un ejemplo útil se encuentra en math.plt también de PLaneT.

  ; Within a (with-modulus n form1 ...) the return values of
  ; the arithmetival operations +, -, * and ^ are automatically
  ; reduced modulo n. Furthermore (mod x)=(modulo x n) and
  ; (inv x)=(inverse x n).

  ; Example: (with-modulus 3 (^ 2 4)) ==> 1

  (define-syntax (with-modulus stx)
    (syntax-case stx ()
      [(with-modulus e form ...)
       (with-syntax ([+   (datum->syntax-object (syntax with-modulus) '+)]
                     [-   (datum->syntax-object (syntax with-modulus) '-)]
                     [*   (datum->syntax-object (syntax with-modulus) '*)]
                     [^   (datum->syntax-object (syntax with-modulus) '^)]
                     [mod (datum->syntax-object (syntax with-modulus) 'mod)]
                     [inv (datum->syntax-object (syntax with-modulus) 'inv)])
         (syntax (let* ([n e]
                        [mod    (lambda (x)   (modulo x n))]
                        [inv    (lambda (x)   (inverse x n))]
                        [+      (compose mod +)]
                        [-      (compose mod -)]
                        [*      (compose mod *)]
                        [square (lambda (x) (* x x))]
                        [^      (rec ^ (lambda (a b)
                                         (cond
                                           [(= b 0)   1]
                                           [(even? b) (square (^ a (/ b 2)))]
                                           [else      (* a (^ a (sub1 b)))])))])
                   form ...)))]))

Comenzaré a responder la última pregunta. Cuándo usar una macro en lugar de una función. Las macros hacen cosas que las funciones no pueden, y las funciones hacen cosas que las macros no pueden hacer, por lo que será difícil mezclarlas, pero vamos a profundizar.

Usas funciones cuando quieres evaluar los argumentos y macros cuando quieres que los argumentos no sean evaluados. Eso no es muy útil, ¿verdad? Utiliza macros cuando quiere escribir algo de una manera diferente, cuando ve un patrón y quiere abstraer. Por ejemplo: defino tres funciones llamadas foo-create, foo-process y foo-destroy para diferentes valores de foo y con cuerpos similares donde el único cambio es foo. Hay un patrón pero un nivel demasiado alto para una función, por lo que crea una macro.

En mi humilde experiencia, las macros en Scheme deben usarse tanto como en otros Lisps, como Common Lisp o Clojure . Supongo que es una prueba de que quizás las macros higiénicas no son una buena idea, y aquí no estaría de acuerdo con Paul Graham sobre por qué. No es porque a veces quieras estar sucio (no higiénico) sino porque las macros higiénicas terminan siendo complejas o complicadas.

Practical Common Lisp, de Peter Seibel, tiene una buena introducción a las macros. En Lisp, por Paul Graham, podría ser una buena fuente de ejemplos más complicados. Además, eche un vistazo a las macros integradas en, por ejemplo, Common Lisp.

El Autómatas a través de macros paper presenta una perla de programación funcional sobre la implementación de máquinas de estados finitos a través de macros en Scheme.

El libro The Reasoned Schemer termina con un Implementación completa basada en macro de miniKanren, el lenguaje de programación lógica utilizado en el libro. Este documento presenta miniKanren y su implementación de manera más formal y concisa que en el libro.

Un ejemplo de una macro más avanzada que no es una forma lambda disfrazada es la macro with-slots de Common Lisp, que hace que el acceso a las ranuras de objetos parezca un acceso variable ordinario:

(with-slots (state door) car
  (when (eq state :stopped)
    (setq state :driving-around)
    (setq door :closed)))

Tenga en cuenta que esto no es lo mismo que vincular los valores de las ranuras a las variables locales y acceder a ellas, ya que with-slots le permite modificar las ranuras mediante SETQ y ver los cambios externos de inmediato.

Tenía una macro curry cuando solía hacer muchos esquemas en mi palma. Fue bastante útil.

Las macros de esquema le permiten agregar características que los autores del idioma original no incluyeron; esa es toda la filosofía detrás de las macros.

Aquí hay un pequeño ejemplo: PLT Scheme proporciona un lenguaje para escribir presentaciones llamado Presentación de diapositivas. Utilicé macros para asociar un número de diapositiva con una diapositiva para poder administrarlas más fácilmente.

I & # 8217; he escrito una macro que proporciona sintaxis infija. Nada muy elegante; sin precedencia Mientras que & # 8217; m en general está bien con la sintaxis de prefijo, prefiero infijo para & Lt; y > ;.

Los uso cuando los procedimientos no son suficientes.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top