Perdón por el título torpe, pero estoy teniendo dificultades para describir lo que estoy buscando en pocas palabras ...

Estoy trabajando en un proyecto Common Lisp DSL y me pregunto si es posible lo siguiente:

El DSL podría tener un par de funciones

(defun foo (&rest rest))

Y

(defun bar (arg))

Que se usará de la siguiente manera:

(foo (bar 100) (bar 200) (bar 300) (bar 400) (bar 500)) etc.

Ahora, eso es una gran cantidad de tipeo redundante, así que me pregunto si es posible crear una macro expansora que permita

(foo (expand bar 100 200 300 400 500))

Sin cambiar foo en sí mismo?

0
EllipsenPark 19 abr. 2020 a las 19:43

3 respuestas

La mejor respuesta

No, esto no se puede hacer sin cambiar la firma de las funciones que está definiendo (o posiblemente usando algo peludo que redefine la macroexpansión): tiene lo que yo llamo un 'desajuste de impedancia de dispersión / dispersión' que no se puede resolver con Una macro estándar en CL.

Una función 'nospread' es una función que envuelve todos sus argumentos en uno formal. Una función 'spread' tiene una formal por argumento. Aprendí estos términos al usar InterLisp: pueden ser anteriores a eso, pero ahora parecen estar en su mayoría sin usar. Las funciones CL solo pueden estar parcialmente dispersas ((foo bar &rest more)). Tu foo está extendido.

Un desajuste de impedancia de propagación / dispersión es cuando tiene una función de propagación pero desea una función de dispersión, o viceversa . Casi siempre es un signo de un problema de diseño. La solución para los problemas de propagación / difusión generalmente implica apply.

Su problema es que foo es una función de difusión: convierte todos sus argumentos en una lista, pero desea que la macro la trate como una función de propagación, entregándole una sola lista de argumentos.

En particular, en CL una expresión como (x (y ...)) nunca se puede convertir en (x a1 a2 ...) para ninguna y, función o macro (pero ver más abajo), y esto es lo que necesita que suceda.

En tu caso quieres algo como

(foo (expand bar a b ...)

Convertirse en

(foo (bar a) (bar b)

Y eso no puede suceder.

Ha habido Lisps que tenían cosas que se llamaban 'macros de empalme' en las que la expansión de una macro podía 'empalmarse' en una lista, de la forma en que ,@ lo hace para la cita inversa. Puede ser que haya paquetes de macro de empalme para CL, e incluso puede ser que sean portátiles: puede obtener un largo camino con *macroexpand-hook*. Pero las macros CL estándar no pueden hacer esto.

1
tfb 20 abr. 2020 a las 10:01

Puede valer la pena dividir su dsl en dos partes, una versión más simple contra la que programa y una versión más fácil de usar. Por ejemplo:

(in-package #:impl)
(defun foo (&rest args) ...)
(defun bar (arg) ...)

(in-package #:dsl)
(defun foo (&rest args) (apply #'impl:foo (append args)))
(defun bar (&rest args) (mapcar #'impl:bar args))
(foo (bar 100 200 300 400 500))
0
Dan Robertson 20 abr. 2020 a las 22:14

No hay necesidad de una macro:

(defun foo (&rest rest)
  (apply #'+ rest)) ; for example add all together 

(defun bar (arg)
  (1+ arg)) ; 1+ is only an example here

(apply #'foo (mapcar #'bar '(100 200 300 400 500)))
1
wasserwerk 19 abr. 2020 a las 18:48