лексическая привязка теряется при переключении пакета

Все, что я хотел, это загрузить файл инициализации для swank, который не повлияет на мой lisp, когда он запускается без swank...

Сначала я попробовал #+swank (defun...) в моем файле, который загружается из ccl-init (попробовал это на ccl 1.10 + windows), и вскоре понял, что он получен до загрузки swank (очевидно).

Моя цель — определить простую функцию в :cl-user каждый раз, когда я запускаю swank. Я только что закончил с шикарным add-hook для загрузки моего файла init.lisp, и, поскольку я хочу определить функцию в cl-user, я попробовал это в init.lisp:

(let ((current-package *package*))
  (in-package :cl-user)
  (defun cd (dir)
    (swank:set-default-directory
      (parse-namestring dir)))
  (in-package current-package))

Теперь я не помню, было ли разрешено использование defun в let, но lisp не жалуется на это, а скорее сообщает мне, что символа cur-pck не существует, и кажется, что когда мы переключаем пакет, cur-pck привязка выходит за рамки. Я думал, что cur-pck — это лексическая привязка, и что она должна быть доступна из лексической области, будучи независимой от пакета, я ошибаюсь?

Почему я переключаю пакеты? Я думаю, что загрузка этого файла из swank в какой-то момент инициализации будет определять вещи в каком-то пакете swank, поэтому я хотел сначала попробовать переключиться на cl-user, определить символ функции и переключиться обратно, чтобы позволить swank сделать это. вещь.

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

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


person AlbusMPiroglu    schedule 21.08.2015    source источник


Ответы (3)


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

(defun cl-user::cd (dir)
    (swank:set-default-directory
      (parse-namestring dir)))

Привязка не "теряется". Чтобы проверить себя, добавьте (princ cur-pck) перед формой в пакете.

Если вы попытаетесь оценить (in-package *package*), вы увидите, почему ваш код не может переключать пакеты. Макрос внутри пакета не оценивает свой аргумент. Код, который даст нам код, который мы хотели бы оценить:

(let ((cur-pck *package*))
  (in-package :cl-user)
  (defun cd (dir)
    (swank:set-default-directory
     (1+ 2)))
  (princ cur-pck)
  `(in-package ,cur-pck)) 

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

Придирка к стилю, не используйте аббревиатуры, пишите current-package.

person PuercoPop    schedule 21.08.2015
comment
плюс один за рекомендацию по стилю ;) - person AlbusMPiroglu; 21.08.2015
comment
Будет ли форма просто оценивать данные (in-package something)? - person Sylwester; 23.08.2015
comment
@Sylwester Нет, это было бы тем, на что вы могли бы написать. Я отредактирую его, чтобы сделать его более понятным. - person PuercoPop; 23.08.2015

Переключение пакетов в форме не оказывает прямого влияния на форму

Давайте посмотрим на это:

(in-package "FOO")

(let ((x 10))
  (in-package "BAR")
  (setf x 20))

Какой x установлен на 20? FOO::X или BAR::X?

Ну, это FOO::X. Переключение пакетов во время выполнения не влияет на уже прочитанные символы. Вся форма let читается сразу, и для этого используется значение *package*. Наличие IN-PACKAGE в самой форме не влияет на саму форму.

Символ с префиксом пакета

Если вы хотите использовать символ в определенном пакете, просто напишите префикс пакета:

cl-user:foo   ; if FOO is exported and the package exists

or

cl-user::foo  ; if foo is not exported and the package exists

Например:

(defun cl-user::cd (...) ...)

Вычисления с помощью символов

Вы также можете вычислить новые символы в пакетах, которые вы еще не знаете:

(let ((sym-name "A-NEW-SYMBOL")
      (my-package-name "SOME-EXISTING-PACKAGE"))
  (intern sym-name my-package-name))

Если пакет не существует, вы можете его создать.

Вы также можете установить функцию вычисляемого символа:

(setf (symbol-function (compute-a-function-symbol))
  #'(lambda ()
      'foo))
person Rainer Joswig    schedule 21.08.2015
comment
Спасибо, Райнер, ваше объяснение включает в себя некоторые из моих вопросов, которые я еще не записал :) - person AlbusMPiroglu; 24.08.2015

IN-PACKAGE — это макрос, а не функция. Проблема в вашем коде заключается в том, что (in-package cur-pck) пытается переключиться не на пакет, обозначенный переменной cur-pck, а на пакет с именем CUR-PCK (которого, очевидно, не существует).

Вы можете временно установить пакет с помощью

(let ((*package* (find-package :cl-user)))
  (defun cd (dir)
    ...))

Но опять же, самый простой способ добиться того, что вы делаете, это

(defun cl-user::cd (dir)
  ...)

что полностью устраняет необходимость установки текущего пакета.

person jlahd    schedule 21.08.2015