Common Lisp Macro для расширения списка аргументов, переданного как rest, например: (foo (панель расширения 100 200 300)) => (foo (строка 100) (строка 200) (строка 300)) - PullRequest
0 голосов
/ 19 апреля 2020

Извините за неуклюжее название, но мне сложно описать то, что я ищу, в нескольких словах ...

Я работаю над проектом Common Lisp DSL и интересно, возможно ли следующее:

DSL может иметь несколько функций

(defun foo (&rest rest))

и

(defun bar (arg))

, который будет использоваться следующим образом:

(foo (bar 100) (bar 200) (bar 300) (bar 400) (bar 500)) et c.

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

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

без изменения самого foo?

Ответы [ 3 ]

1 голос
/ 20 апреля 2020

Нет, этого нельзя сделать, не изменив сигнатуру определяемых вами функций (или, возможно, используя какую-то волосатую вещь, которая переопределяет макроразложение): у вас есть то, что я называю «несоответствием импеданса распространения / nospread», которое не может разрешается стандартным макросом в CL.

Функция nospread - это функция, которая упаковывает все свои аргументы в один формальный. Функция «распространение» имеет один формальный аргумент. Я выучил эти термины при использовании InterLisp: они могут предшествовать этому, но сейчас они в основном не используются. Функции CL могут быть только частично распакованными ((foo bar &rest more)). Ваш foo является nospread.

A рассогласование импеданса распространения / nospread происходит, когда у вас есть функция распространения, но вы хотите использовать функцию nospread, или наоборот . Это почти всегда признак проблемы дизайна. Обходной путь для проблем распространения / nospread обычно включает apply.

Ваша проблема в том, что foo - это nospread-функция: она превращает все свои аргументы в один список, но вы хотите, чтобы макрос обрабатывал ее как Функция распространения, передавая ей один список аргументов.

В частности, в CL выражение, подобное (x (y ...)), никогда не может быть превращено в (x a1 a2 ...) для любой y, функции или макроса (но см. ниже) и это то, что вам нужно.

В вашем случае вы хотите, чтобы что-то вроде

(foo (expand bar a b ...)

превратилось в

(foo (bar a) (bar b)

И этого не может быть .

Были Лиспы, у которых были вещи, которые назывались «сплайсинговыми макросами», в которых расширение макроса можно было «сплайсировать» в список, как это делает ,@ для обратной цитаты. Может случиться так, что для CL существуют макрокоманды сращивания, а может быть и то, что они переносимы: вы можете получить длинный путь с *macroexpand-hook*. Но стандартные CL-макросы не могут этого сделать.

1 голос
/ 19 апреля 2020

Макрос не нужен:

(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)))
0 голосов
/ 21 апреля 2020

Возможно, стоит разделить ваш dsl на две части: более простую версию, для которой вы программируете, и более удобную для пользователя версию. Например:

(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))
...