Во-первых, ваш макрос слишком сложный, вам не нужно отправлять вызов для применения и создавать промежуточный список аргументов, если вы заранее знаете, сколько аргументов будет использовано.Вот еще одна версия:
(defmacro lambda-format ((&rest args) template)
`(lambda ,args (format nil ,template ,@args)))
Вы можете избавиться от макросов, используя функции с переменными числами и APPLY
, но это означает, что, проверяя только сгенерированную функцию (используя, например, проверять или описывать), вы не можете знать,заранее, сколько аргументов необходимо:
(defun curry-format (template)
(lambda (&rest args)
(apply #'format nil template args)))
В случае формата вы можете использовать макрос FORMATTER
, который может анализировать формат шаблона и предупреждатьвы до вы даете ему аргументы во время выполнения:
(defmacro template ((&rest args) template)
(let ((format-fn (gensym))
(template-fn (copy-symbol :template)))
`(let ((,format-fn (formatter ,template)))
(flet ((,template-fn ,args (funcall ,format-fn nil ,@args)))
(function ,template-fn)))))
Здесь я использую FLET, чтобы сгенерированная функция имела понятное имя, но вы также можете использовать лямбду.
(template (a b) "~x ~b")
#<FUNCTION (FLET "TEMPLATE") {1002B93D0B}>
Если вы назовите его, то можете увидеть, что подпись является точной:
Lambda-list: (A B)
Это не относится к варианту с переменным числом.
Макрос ожидает строковые литералы и может проверять, содержит ли он допустимый формат во время макроразложения:
(template (a b) "~x ~!")
;; error in FORMAT: Unknown directive (character: EXCLAMATION_MARK)
;; ~x ~!
Учитывая, как указано FORMATTER
, вы не будете предупреждены, если число фактических аргументов отличается от числаожидаемых аргументов.Вы получите ошибку во время выполнения, если задано слишком мало заданных аргументов, и список неиспользуемых аргументов в качестве возвращаемого значения, если задано слишком много заданных аргументов (этот список также можно проверить, чтобы выдать ошибку).