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

Почему функция/макродихотомия?

Почему функция/макродиктомия присутствует в Common Lisp?

Каковы логические проблемы при разрешении одного и того же имени, представляющего как макрос (имеющий приоритет при обнаружении в позиции функции в compile/eval), так и функцию (используемую, например, с mapcar)?

Например, имея second, определяемый как макрос, и как функция, позволит использовать

(setf (second x) 42)

и

(mapcar #'second L)

не создавая никаких трюков setf.

Конечно, понятно, что макросы могут выполнять больше, чем функции, поэтому аналогия не может быть полной (и я не думаю, конечно, что каждый макросъемка также является функцией), но почему это запрещено путем совместного использования одного пространства имен, когда это может быть полезно?

Надеюсь, я никого не обижу, но я действительно не нахожу "Зачем это делать?" ответ действительно уместен... Я ищу, почему это плохая идея. Наложение произвольного ограничения, потому что не известно хорошее использование, IMO несколько высокомерен (вроде бы предполагает идеальную предвидения).

Или существуют ли практические проблемы в его разрешении?

4b9b3361

Ответ 1

Я думаю, что Common Lisp два пространства имен (функции и значения), а не три (макросы, функции и значения), являются историческими непредвиденными обстоятельствами.

Early Lisps (в 1960-х годах) представляли функции и значения по-разному: значения как привязки в стеке времени выполнения и функционируют как свойства, привязанные к символам в таблице символов. Эта разница в реализации привела к спецификации двух пространств имен, когда Common Lisp был стандартизован в 1980-х годах. См. Статью Ричарда Габриэля Технические вопросы разделения в ячейках функций и ячейках значений для объяснения этого решения.

Макросы (и их предки, FEXPRs, функции, которые не оценивают их аргументы) были сохранены во многих реализациях Lisp в таблице символов, так же как и функции. Было бы неудобно для этих реализаций, если было указано третье пространство имен (для макросов) и вызвало бы проблемы с обратной совместимостью для многих программ.

Подробнее см. статью Kent Pitman Специальные формы в Lisp, чтобы узнать об истории FEXPR, макросов и других специальных форм.

(Примечание: сайт Kent Pitman не работает для меня, поэтому я связался с документами через archive.org.)

Ответ 2

Макросы и функции - это две разные вещи:

  • макросы используют исходный код (!!!) и генерируют новый код источника (!!!)

  • функции являются параметризованными блоками кода.

Теперь мы можем смотреть на это с нескольких углов, например:

a), как мы разрабатываем язык, где функции и макросы четко идентифицируются и выглядят по-разному в нашем исходном коде, поэтому мы (человек) можем легко понять, что это такое?

или

b), как мы смешиваем макросы и функции таким образом, чтобы результат был наиболее полезным и имел самые полезные правила, контролирующие его поведение? Для пользователя не должно иметь значения использовать макрос или функцию.

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

Для функций у нас есть такие задачи, как: определение функции, объем функций, срок службы функций, передача функций вокруг, возвращаемые функции, вызывающие функции, затенение функций, расширение функций, удаление определения функции, компиляции и интерпретация функций,...

Если бы мы сделали макросы похожими на функции, нам нужно решить большинство или все вышеперечисленные проблемы для них.

В вашем примере вы указываете форму SETF. SETF - это макрос, который анализирует закрытую форму при увеличении времени макросов и генерирует код для сеттера. Это мало связано с тем, что SECOND является макросом или нет. Если SECOND будет макросом, в этой ситуации вообще не поможет.

Итак, что такое пример проблемы?

(defmacro foo (a b)
  (if (and (numberp b) (zerop b))
      a
    `(- ,a ,b)))

(defun bar (x list)
  (mapcar #'foo (list x x x x) '(1 2 3 4)))

Что теперь делать? Интуитивно это выглядит просто: карта FOO по спискам. Но это не так. Когда был разработан Common Lisp, я бы предположил, что не ясно, что это должно делать и как оно должно работать. Если FOO - это функция, то было ясно: Common Lisp взял идеи из Схемы за лексически охваченные первоклассные функции и интегрировал их в язык.

Но первоклассные макросы? После разработки Common Lisp в эту проблему вложила кучу исследований и исследовала его. Но во время разработки Common Lisp не было широко распространенного использования первоклассных макросов и опыта работы с проектными подходами. Common Lisp стандартизирует то, что было известно в то время, и то, что, по мнению пользователей, было необходимо разработать (объектная система CLOS - это своего рода роман, основанный на более раннем опыте использования подобных объектных систем). Общий Lisp не был разработан, чтобы иметь теоретически наиболее приятный диалект Lisp - он был разработан с мощным Lisp, который позволяет эффективно реализовывать программное обеспечение.

Мы могли бы обойти это и сказать, что передача макросов невозможна. Разработчик должен был бы предоставить функцию под тем же именем, которое мы передаем.

Но тогда (funcall #'foo 1 2) и (foo 1 2) будут ссылаться на разные механизмы? В первом случае функция FOO, а во втором случае мы используем макрос FOO для генерации кода для нас? В самом деле? Хотим ли мы (как программисты-человеки) этого? Я думаю, что нет - похоже, что программирование намного сложнее.

С прагматичной точки зрения: макросы и механизм, лежащие в ее основе, уже достаточно сложны, что большинство программистов испытывают трудности с этим в реальном коде. Они делают отладку и понимание кода намного сложнее для человека. На первый взгляд макрос упрощает чтение кода, но цена - это необходимость понять процесс и результат расширения кода. Найти способ дальнейшей интеграции макросов в дизайн языка - непростая задача.

readscheme.org имеет некоторые ориентиры в исследованиях, связанных с макросом, по адресу. Схема: Макросы

Как насчет Common Lisp

Общие Lisp предоставляет функции, которые могут быть первоклассными (хранятся, передаваться и...) и лексически именуемыми для них именами (DEFUN, FLET, LABELS, FUNCTION, LAMBDA > ).

Общие Lisp предоставляет глобальные макросы (DEFMACRO) и локальные макросы (MACROLET).

Общие Lisp предоставляет глобальные макросы компилятора(DEFINE-COMPILER-MACRO).

С макросами компилятора можно иметь функцию или макрос для символа И макроса компилятора. Система Lisp может решить предпочесть макрос компилятора над макросом или функцией. Он также может игнорировать их полностью. Этот механизм в основном используется для того, чтобы пользователь мог запрограммировать определенные оптимизации. Таким образом, он не решает каких-либо проблем, связанных с макросом, но обеспечивает прагматичный способ программирования глобальных оптимизаций.

Ответ 3

Потому что тогда одно и то же имя будет представлять два разных объекта, в зависимости от контекста. Это делает программу излишне трудной для понимания.