Переопределение "defun" в пакете

Я хотел бы определить макрос с именем "defun" из пакета, который я создаю, и я хотел бы экспортировать его для использования в определенных местах. Есть библиотека под названием parenscript, которая делает это в своем пакете следующим образом:

(export #:defun)

Когда я пытаюсь сделать это в моем собственном пакете, я получаю эту ошибку SBCL

Lock on package COMMON-LISP violated when defining DEFUN as a macro while in package COMMON-LISP-USER.

Как это делается в библиотеке parenscript? Я знаю, что вы можете напечатать форму;

(ps (defun function-name (args) (body)))

Я хочу быть в состоянии сделать то же самое, но не могу понять, как это сделать?

2 ответа

Решение

Вы хотите затенить оригинальный символ из пакета CL.

CL-USER 1 > (defpackage "MY-PACKAGE" (:use "CL"))
#<The MY-PACKAGE package, 0/16 internal, 0/16 external>

CL-USER 2 > (in-package "MY-PACKAGE")
#<The MY-PACKAGE package, 0/16 internal, 0/16 external>

MY-PACKAGE 3 > (shadow 'defun)
T

MY-PACKAGE 4 > (cl:defun defun () :my-defun-returns)
DEFUN

MY-PACKAGE 5 > (defun)
:MY-DEFUN-RETURNS

MY-PACKAGE 6 > (export 'defun)
T

Вам нужно прочитать больше о пакетах и ​​символах. Здесь я собираюсь квалифицировать все символы, когда это необходимо, чтобы не было двусмысленности относительно того, о чем я говорю.

  1. Вы не можете переопределить CL:DEFUNэто вызывает неопределенное поведение, и вы, скорее всего, "сломаете" свою среду выполнения, сделав ее непригодной для использования. Вот почему в SBCL существует концепция блокировок для пакетов, которая позволяет избежать ошибочного изменения пакета и его привязок (вы все равно можете разблокировать пакет, который обычно не требуется).

  2. В рамках вашего макроса, вы можете интерпретировать CL:DEFUN как вы хотите, именно это и делает Parenscript, переводя подмножество фактического кода на Лиспе в Javascript.

  3. В любой другой упаковке PВы можете определить P:DEFUN как переменная / функция / макрос / все, что полностью отличается от CL:DEFUN, Вы можете экспортировать его, и все в порядке, вы можете использовать оба P:DEFUN а также CL:DEFUN как вы хотите.

  4. Конфликты могут возникнуть, если вы хотите написать неквалифицированную DEFUN Символ и пусть читатель узнает, на какие символы ссылаются. Как правило, пользователи библиотеки могут определять пакет следующим образом:

    (defpackage :foo (:use :cl :p))
    

    Это приводит к конфликту, потому что и пакеты "CL", и пакеты "P" экспортируют "DEFUN". Один из способов решить эту проблему - определить диалект Common Lisp, который перепривязывает DEFUN и повторно экспортирует все другие символы из "CL". Затем ваши пользователи должны использовать только ваш пакет, а не пакет CL. Другой способ - использовать CL и shadow-import только "DEFUN" из P, так что DEFUN это псевдоним для P:DEFUN (следовательно, вам нужно написать CL:DEFUN явно ссылаться на макрос Common Lisp).

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

Другие вопросы по тегам