Переопределение специального оператора с помощью MACROLET?

Я пытаюсь переопределить IF локально (цель - встроить DSL).

Поведение меня действительно смущает. В СБКЛ:

(macrolet ((if (x) x)) 
  (if 'x)) ; => X

(macrolet ((if (x) x)) 
  (let ((x 2)) 
    (if 'x))) ; => Execution of a form compiled with errors.

(let ((x 2)) 
  (macrolet ((if (x) x)) 
    (if 'x))) ; => Execution of a form compiled with errors.

Есть идеи, почему он так себя ведет?

Я также пытался использовать интернированный символ xxx::if, но безуспешно, SBCL все еще думает, что это специальный оператор, в каком бы пакете он ни находился.

Можно ли как-то правильно переопределить специальный оператор локально? Или, по крайней мере, буквально переопределить, что означает, что можно ввести if, и оно будет расширено (может помочь использование некоторой магии чтения, но я не знаком с этим).

Обновление (решено): точное сообщение об ошибке для второго, которое я получал

Execution of a form compiled with errors.
Form:
  (MACROLET ((IF (X)
             X))
  (IF 'X))
Compile-time error:
  Lock on package COMMON-LISP violated when binding IF as a local macro while in
package COMMON-LISP-USER.
See also:
  The SBCL Manual, Node "Package Locks"
  The ANSI Standard, Section 11.1.2.1.2
   [Condition of type SB-INT:COMPILED-PROGRAM-ERROR]

3-й аналогично.

Оказывается, использование xxx::if заставляет это работать. Я просто неправильно затенил символ в предыдущем эксперименте.

Простой способ заставить это работать:

(defpackage p)
(macrolet ((p::if (x) x)) 
  (let ((x 2)) 
    (p::if 'x))) ; => X

Если вы хотите наследовать другие обычные операторы CL в P, просто выполните (defpackage p (:use :cl) (:shadow if)).


person BlueFlo0d    schedule 29.02.2020    source источник
comment
Я получаю сообщение об ошибке из первого, блокировка пакета COMMON-LISP нарушена при привязке IF как локального макроса   -  person Barmar    schedule 29.02.2020
comment
Если я использую p::if, я не получаю ошибок от 2-й и 3-й версии, только предупреждения о неиспользуемой переменной X. Это понятно, потому что вы цитируете x в теле, а не используете переменную.   -  person Barmar    schedule 29.02.2020
comment
Оба они возвращают 2 без предупреждений, если я удалю кавычку перед x.   -  person Barmar    schedule 29.02.2020
comment
Хорошо, это интересно, раньше я делал sb-ext:unlock-package :cl. Возможно, какое-то вмешательство в блокировку пакетов? Позвольте мне попробовать со свежим REPL.   -  person BlueFlo0d    schedule 29.02.2020
comment
Я все еще получаю ту же ошибку, когда запускаю (let ((x 2)) (macrolet ((cl-user::if (x) x)) (cl-user::if 'x))) в новом обновлении. Я на SBCL 1.5.1. Зависит ли эта реализация?   -  person BlueFlo0d    schedule 29.02.2020
comment
Не используйте cl-user::if, создайте свой собственный пакет и используйте mypackage::if   -  person Barmar    schedule 29.02.2020
comment
Хорошо, (defpackage p (:use :cl) (:shadow if)) (macrolet ((p::if (x) x)) (p::if 'x)) теперь у меня работает. Большое спасибо!   -  person BlueFlo0d    schedule 29.02.2020
comment
Я только что сделал (make-package 'p), а затем использовал p::if. Нет необходимости наследовать или скрывать что-либо.   -  person Barmar    schedule 29.02.2020
comment
Пожалуйста, отредактируйте вопрос и покажите точное сообщение об ошибке, которое вы получили.   -  person Barmar    schedule 29.02.2020
comment
Конечно, я также хочу использовать некоторые другие материалы, предоставленные CL, в DSL (поэтому я хочу их встроить). Хорошо, я обновляю вопрос сейчас.   -  person BlueFlo0d    schedule 29.02.2020
comment
Обратите внимание, что (defpackage p) и (make-package 'p) недостаточно указаны для переносимого Common Lisp. Неизвестно, какие пакеты они используют. SBCL, как известно, не использует ни одного из них.   -  person Rainer Joswig    schedule 29.02.2020


Ответы (1)


Привязка символа if (тот, что в пакете Common Lisp, common-lisp:if) является неопределенным поведением .

Если вы хотите создать язык макросов, который использует некоторые из тех же имен символов, что и стандартный Common Lisp, лучше всего использовать другой пакет, чтобы то, что выглядит как if, на самом деле было mypackage:if.

Более подробно: в разделе 11.1.2.1.2 указано, что любое связывание символов пакета Common Lisp является неопределенным поведением, кроме случаев, когда это явно разрешено.

Связывание символа Common Lisp с macrolet явно разрешено в подпункте 11.1.2.1.2.1:

"Если внешний символ пакета COMMON-LISP не определен как стандартизированная функция, макрос или специальный оператор, его можно лексически связать как макрос (например, с помощью макролета).

Так, например, поскольку константа pi (т.е. cl:pi) не является функцией, макросом или специальным оператором, (macrolet ((pi ...)) ...) может быть оценена соответствующей программой.

person Kaz    schedule 02.03.2020