Подтвердить что ты не робот

Определение функции defmacro с использованием только примитивов LISP?

Маккарти Элементарные S-функции и предикаты были atom, eq, car, cdr, cons

Затем он добавил к своим основным обозначениям, чтобы записать то, что он назвал S-функциями: quote, cond, lambda, label

На этом основании мы будем называть эти "примитивы LISP" (хотя я открыт для аргумента о типичных предикатах, таких как numberp)

Как бы вы определили функцию defmacro, используя только эти примитивы в LISP по вашему выбору? (включая Схему и Clojure)

4b9b3361

Ответ 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 вокруг еще нескольких.

Ответ 2

Объясняя это полностью во всех его деталях, для этого здесь потребуется очень много места и времени, но схема очень проста. Каждый LISP имеет в своем ядре что-то вроде цикла READ-EVAL-PRINT, который должен сказать что-то, что принимает список, элемент за элементом, интерпретирует его и изменяет состояние - либо в памяти, либо путем печати результата.

В прочитанной части рассматривается каждый элемент, который читается и что-то с ним делает:

(cond ((atom elem)(lambda ...))
      ((function-p elem) (lambda ...)))

Чтобы интерпретировать макросы, вам просто (?) нужно реализовать функцию, которая помещает текст шаблона макроса где-то в хранилище, предикат для этого цикла реплики - что означает просто определение функции - которая говорит: "О, это макрос!", а затем скопируйте этот текст шаблона обратно в читатель, чтобы он интерпретировался.

Если вы действительно хотите увидеть волосатые детали, прочитайте "Структура и интерпретация компьютерных программ" или прочитайте "Queinnec Lisp в Small PIeces.