OP определил макрос op
, как если бы он был функцией, но макросы Lisp не работают таким образом. Форма макроса оценивается для создания новой формы, которая заменяет исходный вызов макроса, но аргументы макроса передаются в тело макроса без оценки . Это означает, что в теле макроса op
, car
работает не с данными (+ 1 2)
, а с данными (quote (+ 1 2))
.
Цель макроса op
- не оцените (car expr)
, но для получения формы (car expr)
(где expr
заменяется значением аргумента макроса), который затем оценивается в REPL после того, как произошло расширение макроса. Это можно сделать либо с помощью list
:
(define-macro (opl expr)
(list 'car expr))
, либо с помощью квазиквотация :
(define-macro (opq expr)
`(car ,expr))
Здесь обратная цитата представляет шаблон для списка, и запятая заставляет символ expr
быть оцененным до его значения ((quote (+ 1 2))
), и результат вставляется в список. Простой список в кавычках, например, '(car expr)
будет соответствовать списку (car expr)
, где expr
это просто символ expr
. При квазиквотации ,expr
оценивается как значение аргумента, предоставленного при вызове макроса, например, `(car ,expr)
-> (car '(+ 1 2))
. Обратите внимание, что (list 'car expr)
выдает ту же форму, когда expr
равен '(+ 1 2)
, как с (opl '(+ 1 2))
Этот синтаксис define-macro
почти идентичен традиционному синтаксису defmacro
Common Lisp, разница при этом с defmacro
имя макроса идет перед списком формальных параметров, например, (defmacro op (expr) ;...)
. define-macro
недоступно в стандартной схеме, но некоторые реализации схемы do поддерживают ее. Схема Guile поддерживает как defmacro
, так и define-macro
. Оба вышеперечисленных макро-решения работают в Guile:
scheme@(guile-user)> (opl '(+ 1 2))
$2 = +
scheme@(guile-user)> (opq '(+ 1 2))
$3 = +