Ответ 1
Проблема с попыткой сделать это на машине, такой как машина McCarthy LISP, заключается в том, что не существует способа предотвратить оценку аргументов во время выполнения, и нет способа изменить ситуацию во время компиляции (что и есть макросы do: они переупорядочивают код до его компиляции, в основном).
Но это не мешает нам переписывать наш код во время выполнения на машине Маккарти. Трюк состоит в том, чтобы процитировать аргументы, которые мы передаем нашим "макросам", чтобы они не оценивались.
В качестве примера рассмотрим функцию, которую мы хотели бы иметь; 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, все будет хорошо. Проблема в том, что в нашем современном Lisps мы не просто имеем побочный эффект, поэтому тот факт, что все аргументы, переданные на unless
, оцениваются, независимо от того, хотим мы их или нет, проблематично. На самом деле, даже в McCarthy LISP, это может быть проблемой, если, скажем, оценка одного из аргументов потребовала времени, и мы хотели бы сделать это редко. Но это особенно проблема с побочными эффектами.
Итак, мы хотим, чтобы наш unless
оценивал и возвращал q
, только если p
- false. Этого мы не сможем сделать, если передать 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 вокруг еще нескольких.