Проблема с попыткой сделать это на компьютере, подобном машине LISP Маккарти, заключается в том, что нет способа предотвратить вычисление аргумента во время выполнения, и нет способа изменить ситуацию во время компиляции (что и делают макросы: в основном они переставляют код перед его компиляцией).
Но это не мешает нам переписывать наш код во время выполнения на машине Маккарти. Хитрость заключается в том, чтобы процитировать аргументы, которые мы передаем нашим «макросам», чтобы они не оценивались.
В качестве примера давайте рассмотрим функцию, которую мы могли бы хотеть иметь; unless
. Наша теоретическая функция принимает два аргумента, p
и q
, и возвращает q
, если p
не является истинным. Если p
истинно, вернуть ноль.
Некоторые примеры (в синтаксисе Clojure, но это ничего не меняет):
(unless (= "apples" "oranges") "bacon")
=> "bacon"
(unless (= "pears" "pears") "bacon")
=> nil
Итак, сначала мы могли бы написать unless
как функцию:
(defn unless [p q]
(cond p nil
true q))
И это, кажется, работает нормально:
(unless true 6)
=> nil
(unless false 6)
=> 6
А с LISP Маккарти это будет работать просто отлично. Проблема в том, что в нашем современном Лиспе нет не имеющего побочных эффектов кода, поэтому факт, что все аргументы, переданные в unless
, оцениваются независимо от того, хотим мы этого или нет, проблематичен. На самом деле, даже в LISP Маккарти это могло быть проблемой, если бы, скажем, для оценки одного из аргументов потребовалось возраст , а мы бы хотели делать это редко. Но это особенно проблема с побочными эффектами.
Итак, мы хотим, чтобы наши unless
оценивали и возвращали q
только , если p
ложно. Этого мы не сможем сделать, если передадим q
и p
в качестве аргументов функции.
Но мы можем quote
их до того, как мы передадим их в нашу функцию, предотвращая их оценку. И мы можем использовать мощность eval
(также определенную, используя только примитивы и другие функции, определенные с примитивами позже в упомянутой статье), чтобы оценить, что нам нужно, когда нам нужно.
Итак, у нас есть новый unless
:
(defn unless [p q]
(cond (eval p) nil
true (eval q)))
И мы используем его немного по-другому:
(unless (quote false) (quote (println "squid!")))
=> "squid" nil
(unless (quote true) (quote (println "squid!")))
=> nil
И у вас есть то, что можно щедро назвать макросом.
Но это не defmacro
или эквивалент в других языках. Это потому, что на машине Маккарти не было способа выполнить код во время компиляции. И если вы оценивали свой код с помощью функции eval
, он не мог знать, что не нужно оценивать аргументы для «макро» -функции. Между чтением и оценкой не было такого различия, как сейчас, хотя идея была в этом. Была возможность «переписать» код, в кругу quote
и в операциях со списком вместе с eval
, но он не был интернирован в языке, как сейчас (я бы назвал его синтаксическим почти, просто: просто процитируй свои аргументы, и ты получишь силу макросистемы.)
Надеюсь, я ответил на ваш вопрос, не пытаясь определить приличных defmacro
с этими примитивами сам. Если вы действительно хотите это увидеть, я бы указал вам на источник с трудом для подхвата для defmacro
в источнике Clojure или Google вокруг него.