11.7 Функции и переменные для системы пакетов

Для описанных здесь функций, все необязательные аргументы с именем package имеют значение по-умолчанию *package*. Там, где функция принимает аргумент, который может быть символом или списком символов, значение nil расценивается, как пустой список символов. Любой аргумент, описанный как имя пакета, может быть символом или строкой. Если указан символ, то используется его имя. Если строка, то пользователь должен позаботиться о преобразовании регистра символов в верхний там, где это необходимо.

[Переменная] *package*

Значение этой переменной должно быть объектом пакета. Этот пакет называется текущим. Первоначальное значение *package* является пакетом common-lisp-user.

Функции load и compile-file пересвязывают *package* в текущее значение. Если некоторая форма в файле во время загрузки изменяет значение *package*, старое значение будет восстановлено после завершения загрузки.

Функция compile-file пересвязывает *package* в текущее значение. Если некоторая форма в файле во время загрузки или компиляции изменяет значение *package*, старое значение будет восстановлено после завершения функции compile-file.


[Функция] make-package package-name &key :nicknames :use

Эта функция создаёт и возвращает новый пакет с указанным именем. Как было описано выше, аргументом может быть или символ, или строка. Аргумент :nicknames должен быть списком строк, которые будут псевдонимами. И здесь пользователь вместо строк может указывать символы, в случае которых будут использоваться их имена. Это имя и псевдонимы не могут конфликтовать с уже имеющимися именами пакетов. Если конфликт произошёл, сигнализируется исправимая ошибка.

Аргумент :use является списком пакетов или имён (строк или символов) пакетов, чьи внешние символы будут унаследованы новым пакетом. Эти пакеты должны существовать перед вызовом функции.


[Макрос] in-package name

Этот макрос пересвязывает переменную *package* с пакетом, имя которого указано в параметре name. Параметр name может быть строкой или символом. Форма name не вычисляется. В случае отсутствия пакета, сигнализируется ошибка. Кроме того, в случае вызова в качестве формы верхнего уровня, этот макрос работает и во время компиляции.

in-package возвращает новый пакет, то есть значение *package* после выполнения операции.


[Функция] find-package name

Параметр name должен быть строкой, которая является именем или псевдонимом искомого пакета. Этот параметр может также быть символов, в случае которого используется имя. В результате возвращается объект пакета с указанным именем или псевдонимом. Если пакет найден не был, то find-package возвращает nil. Сравнение имён регистрозависимо (как в string=).

Аргумент package может быть объектом пакета, в таком случае значение аргумента сразу возвращается. Смотрите раздел 11.2.


[Функция] package-name package

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

Кроме того функция принимает имя пакета или псевдоним, и в этом случае возвращает главное имя пакета. Смотрите раздел 11.2.

package-name возвращает nil вместо имени пакета, если пакет был удалён. Смотрите delete-package.


[Функция] package-nicknames package

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

Кроме того функция принимает имя пакета или псевдоним, и в этом случае возвращается список псевдонимов пакета. Смотрите раздел 11.2.


[Функция] rename-package package new-name &optional new-nicknames

Старое имя и все старые псевдонимы пакета package удаляются и заменяются на new-name и new-nicknames. Аргумент new-name может быть строкой или символом. Аргумент new-nicknames, который по-умолчанию nil, является списком строк или символов.

Аргумент package может быть как объектом пакета, так и его именем. Смотрите раздел 11.2.

rename-package возвращает package.


[Функция] package-use-list package

Данная функция возвращает список пакетов, используемых указанным в параметре пакетом.

Аргумент package может быть как объектом пакета, так и его именем. Смотрите раздел 11.2.


[Функция] package-used-by-list package

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

Аргумент package может быть как объектом пакета, так и его именем. Смотрите раздел 11.2.


[Функция] package-shadowing-symbols package

Данная функция возвращает список символов, которые были задекларированы, как скрывающие символы, с помощью shadow или shadowing-import. Все символы в этом списке является родственными указанному пакету.

Аргумент package может быть как объектом пакета, так и его именем. Смотрите раздел 11.2.


[Функция] list-all-packages

Эта функция возвращает список всех пакетов, которые существуют в Lisp’овой системе.


[Функция] delete-package package

Функция delete-package удаляет указанный в аргументе package пакет, из всех структур данных системы пакетов. Аргумент package может быть как пакетом, так и его именем.

Если package является именем, но пакета с данным именем не существует, сигнализируется исправимая ошибка. Продолжение из этой ошибки не делает попытку удаления, но возвращает nil из вызова delete-package.

Если package является пакетов, но пакет уже был удалён, ошибка не сигнализируется и попыток удаления не происходит. Вместо этого delete-package немедленно возвращает nil.

Если пакет, указанный для удаления, в этот момент используется другими пакетами, сигнализируется исправимая ошибка. Продолжение из этой ошибки выглядит как, если бы функция unuse-package была выполнена на всех использующих пакетах для удаления зависимостей, после чего была вызвана delete-package для удаления пакета.

Если указанный пакет является домашним для некоторого символа, тогда после выполнения delete-package, ячейка домашнего пакета символа становится неопределённой. Никак по-другому символы в удаляемом пакете не модифицируются.

Имя и псевдонимы пакета package перестают быть сами собой. Объект пакета остаётся, но анонимно: packagep будет истинным, но package-name применительно к нему будет возвращать nil.

Влияние любой другой операции на удалённым объектом пакета не определена. В частности, попытка найти символ в удалённый пакет (например, используя intern или find-symbol) будет иметь неопределённый результат.

Если удаление было успешно, delete-package возвращает t, иначе nil.


[Функция] intern string &optional package

В пакете package, который по-умолчанию равен текущему пакету, осуществляется поиск символа с именем, указанным в аргументе string. Этот поиск будет включить унаследованные символы, как описано в разделе 11.4. Если ни одного символа не найдено, то символ создаётся и устанавливается в указанный пакет в качестве внутреннего символа (или внешнего символа, если пакет keyword). Указанный пакет становиться домашним пакетом для созданного символа. Возвращается два значения. Первое является символом, который был найден или создан. Второе значение nil, если символ не существовал ранее, или одно из следующих значений:

Аргумент package может быть как объектом пакета, так и его именем. Смотрите раздел 11.2.


[Функция] find-symbol string &optional package

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

Аргумент package может быть как объектом пакета, так и его именем. Смотрите раздел 11.2.


[Функция] unintern symbol &optional package

Если указанный символ представлен в указанном пакете package, он удаляется из этого пакета и также из списка скрывающих символов, если он там был. Кроме того, если package для символа является домашним пакетом, то символ отсоединяется и становится бездомным. Следует отметить, что в некоторых обстоятельствах может продолжить быть доступным с помощью наследования в указанном пакете. Если удаление действительно произошло, unintern возвращает t, иначе nil.

unintern должна использоваться с осторожностью. Она изменяет состояние системы пакетов так, что правила согласованности перестают действовать.

Аргумент package может быть как объектом пакета, так и его именем. Смотрите раздел 11.2.


[Функция] export symbols &optional package

Аргумент symbols должен быть списком символов или одиночным символом. Эти символы становятся доступными как внешние символы в пакете package (смотрите раздел 11.4). export возвращает t.

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

Аргумент package может быть как объектом пакета, так и его именем. Смотрите раздел 11.2.


[Функция] unexport symbols &optional package

Аргумент symbols должен быть списком символов или одиночным символом. Эти символы становятся внутренними символами в пакете package. Использование этой операции для символов пакета keyword является ошибкой. (смотрите раздел 11.4). unexport возвращает t.

Аргумент package может быть как объектом пакета, так и его именем. Смотрите раздел 11.2.


[Функция] import symbols &optional package

Аргумент symbols должен быть списком символов или одиночным символом. Эти символы становятся внутренними символами в пакете package и, таким образом, могут быть указаны без использования полного имени (с двоеточием). import сигнализирует исправимую ошибку, если любой из импортируемых символов имеет такое же имя, как один из уже присутствующих в пакете символов (смотрите раздел 11.4). import возвращает t.

Если любой импортируемый символ не имеет домашнего пакета, тогда import устанавливает домашний пакет тот, в который символ импортируется.

Аргумент package может быть как объектом пакета, так и его именем. Смотрите раздел 11.2.


[Функция] shadowing-import symbols &optional package

Данная функция похожа на import, но не сигнализирует ошибку, даже если импортирование символа будет скрывать некоторый другой символ, уже присутствующий в пакете. В дополнение к импортированию символ помещается в список скрывающих символов пакета package (смотрите раздел 11.5). shadowing-import возвращает t.

shadowing-import должна использоваться с осторожностью. Она изменяет состояние системы пакетов так, что правила согласованности перестают действовать.

Аргумент package может быть как объектом пакета, так и его именем. Смотрите раздел 11.2.


[Функция] shadow symbols &optional package

Аргумент должен быть списком символов или строк или одиночным символом или строкой. Рассматривается имя каждого символа (или просто строка) и в указанном пакете package ищется символ с таким же именем. Если данный символ присутствует в этом пакете (напрямую, а не с помощью наследования), тогда он добавляется в список скрывающих символов. Иначе, создаётся новый символ с указанным именем, и вставляется в пакет package как внутренний символ. Символ также помещается в список затеняющих символов пакета package (смотрите раздел 11.5). shadow возвращает t.

shadowing должна использоваться с осторожностью. Она изменяет состояние системы пакетов так, что правила согласованности перестают действовать.

Аргумент package может быть как объектом пакета, так и его именем. Смотрите раздел 11.2.


[Функция] use-package packages-to-use &optional package

Аргумент packages-to-use должен быть списком пакетов или их имён или одиночным пакетом или его именем. Эти пакеты добавляются в список использования package, если их там ещё нет. Все внешние символы в пакетах становятся доступными в пакете package как внутренние символы (смотрите раздел 11.4). Использование пакета keyword является ошибкой. use-package возвращает t.

Аргумент package может быть как объектом пакета, так и его именем. Смотрите раздел 11.2.


[Функция] unuse-package packages-to-unuse &optional package

Аргумент packages-to-use должен быть списком пакетов или их имён или одиночным пакетом или его именем. Эти пакеты удаляются из списка использования package, если они там были. unuse-package возвращает t.

Аргумент package может быть как объектом пакета, так и его именем. Смотрите раздел 11.2.


[Макрос] defpackage defined-package-name {option}*

Эта функция создаёт новый пакет, или модифицирует уже существующий, имя которого является defined-package-name. Аргумент define-package-name может быть строкой или символом. Если он является символом, то используется его имя, а не пакет, если вдруг в котором находится данный символ. Созданный или модифицированный пакет возвращается как значение формы defpackage.

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

1.  :shadow and :shadowing-import-from
2.  :use
3.  :import-from and :intern
4.  :export

Сначала устанавливаются затенения, чтобы избежать ложных конфликтов при установке связей для use. Связи use должны устанавливаться перед импортированием и интернированием, таким образом эти операции могут ссылаться на нормально унаследованные символы, а не создавать новые. Экспортирование выполняется последним, таким образом символы созданные любой другой операцией, в частности, затеняющие и импортированные символы, могут быть экспортированы. Следует отметить, что экспортирование унаследованного символа сначала неявно импортирует его (смотрите раздел 11.4).

Если пакета с именем defined-package-name не существовало, то defpackage создаст его. Если такой пакет уже существовал, то новый пакет создан не будет. Существующий пакет модифицируется, если возможно, для отражения нового определения. Результат не определён, если новое определение не совместимо с текущим состоянием пакета.

Если одинаковый аргумент symbol-name (в смысле сравнения имён с помощью string=) встречается более одного раза в опциях :shadow, :shadowing-import-from, :import-from и :intern, сигнализируется ошибка.

Если одинаковый аргумент symbol-name (в смысле сравнения имён с помощью string=) встречается более одного раза в опциях :intern и :export, сигнализируется ошибка.

Другие виды конфликтов обрабатывается в том же стиле, как в соответствующих операциях use-package, import и export.

Реализация может поддерживать другие опции для defpackage. Каждая реализация должно сигнализировать ошибку при встрече с неподдерживаемой опцией для defpackage.

Функция compile-file должна обрабатывается формы верхнего уровня defpackage таким же методом, как и обрабатывает формы верхнего уровня для работы с пакетам (как описано в начале раздела 11.7).

Вот пример вызова defpackage, которая «не рискует (plays it safe)», используя только строки в качестве имён.

(cl:defpackage "MY-VERY-OWN-PACKAGE"
  (:size 496)
  (:nicknames "MY-PKG" "MYPKG" "MVOP")
  (:use "COMMON-LISP")
  (:shadow "CAR" "CDR")
  (:shadowing-import-from "BRAND-X-LISP" "CONS")
  (:import-from "BRAND-X-LISP" "GC" "BLINK-FRONT-PANEL-LIGHTS")
  (:export "EQ" "CONS" "MY-VERY-OWN-FUNCTION"))

defpackage пример выше спроектирован работать корректно, даже если текущий пакет не использует пакет common-lisp. (Следует отметить, что в этом примере используется псевдоним cl для пакета common-lisp.) Более того, выполнение этой формы defpackage не создаст символа в текущем пакете. А также для последующего удобства для строк использованы буквы в верхнем регистре. ______________________________________________________

Заметка для реализации: An implementation of defpackage might choose to transform all the package-name and symbol-name arguments into strings at macro expansion time, rather than at the time the resulting expansion is executed, so that even if source code is expressed in terms of strange symbols in the defpackage form, the binary file resulting from compiling the source code would contain only strings. The purpose of this is simply to minimize the creation of useless symbols in production code. This technique is permitted as an implementation strategy but is not a behavior required by the specification of defpackage.

___________________________________________________________________________________________________________

А вот для контраста пример defpackage, который «играет по крупному (plays the whale)» с использованием всех типов допустимого синтаксиса.

(defpackage my-very-own-package
  (:export :EQ common-lisp:cons my-very-own-function)
  (:nicknames "MY-PKG" #:MyPkg)
  (:use "COMMON-LISP")
  (:shadow "CAR")
  (:size 496)
  (:nicknames mvop)
  (:import-from "BRAND-X-LISP" "GC" Blink-Front-Panel-Lights)
  (:shadow common-lisp::cdr)
  (:shadowing-import-from "BRAND-X-LISP" CONS))

Этот пример производит то же влияние на вновь созданный пакет, но может создавать бесполезные символы в других пакетах. Использования явных тегов пакетов особенно запутаны. Например, эта форма defpackage делает так, что символ cdr будет затенён в новом пакете. Данный символ не будет затенён в пакете common-lisp. Факт, что имя «CDR» было указано полным именем (с двоеточием) является отвлекающим маневром. Мораль в том, что синтаксическая гибкость defpackage, как в других частях Common Lisp’а, даёт большие удобства в использовании в рамках здравого смысла, но создаёт невообразимую путаницу при использовании мальтузианского изобилия.

Следует отметить, что defpackage не способна сама по себе определять взаимно рекурсивные пакеты, например, два пакета использующих друг друга. Однако, ничто не мешает использовать defpackage для выполнения первоначальных установок, а затем использовать такие функции, как use-package, import и export для создания связи.

Цель defpackage поощрять пользователя размещать определение пакета и его зависимостей в одном месте. Это может также позволить проектировщику большой системы размещать определения всех необходимых пакетов (скажем) в один файл, который может быть загружен перед загрузкой или компиляцией кода, который зависит от этих пакетов. Такой файл, если аккуратно сконструирован, может быть просто загружен в пакет common-lisp-user.

Реализации и программные окружения могут также лучше поддерживать процесс разработки (если только предлагая лучшую проверку ошибок) с помощью глобальной базы значений о предполагаемых установках пакетов.


[Функция] find-all-symbols string-or-symbol

find-all-symbols просматривает каждый пакет в Lisp’овой системе для поиска символа, имя которого совпадает с указанной строкой. Список всех найденных символов возвращается в качестве результата. Этот поиск регистрозависимый. Если аргумент является символом, для поиска по строке используется его имя.


[Макрос] do-symbols (var [package [result-form]]){declaration}* {tag | statement}*

do-symbols предоставляет прямой цикл по символам пакета. Тело выполняется единожды для каждого символа доступного в пакете package. Символ связывается с переменной var. Затем вычисляется форма result-form (одиночная форма, не неявный progn), и результат является результатом формы do-symbols. (Когда вычисляется форма result-form, переменная var продолжает быть связанной и имеет значение nil.) Если result-form опущена, результат nil. Для немедленного завершения цикла может использоваться return. Если выполнение тела влияет на то, какие символы содержаться в пакете package, то возможно только удаление символа связанного с переменной var с использованием unintern, иначе результат не определён.

Аргумент package может быть как объектом пакета, так и его именем. Смотрите раздел 11.2.

Тело do-symbols для одного и того же символа может быть выполнено несколько раз, пользователь должен сам следить за этим.

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

(setq *a* (make-package ’a))      ;Неявно использует common-lisp
(setq *b* (make-package ’b))      ;Неявно использует common-lisp
(setq *c* (make-package ’c :use ’(a b)))

(do-symbols (x *c*) (print x))    ;Символы пакета common-lisp
                                  ; могут быть выведены один или два раза

Пользователь ограничен в создании побочных действий так, как это описано в разделе 7.9

Следует отметить, что конструкция loop предоставляет тип выражения for, которое может проходить в цикле по символам пакета (смотрите главу 26).


[Макрос] do-external-symbols (var [package [result]]){declaration}* {tag | statement}*

do-external-symbols похожа на do-symbols за исключением того, что сканируются только внешние символы пакета.

The clarification voted by X3J13 in March 1988 for do-symbols , regarding redundant executions of the body for the same symbol, applies also to do-external-symbols.

Аргумент package может быть как объектом пакета, так и его именем. Смотрите раздел 11.2.

Пользователь ограничен в создании побочных действий так, как это описано в разделе 7.9


[Макрос] do-all-symbols (var [result-form]){declaration}* {tag | statement}*

Эта функция похожа на do-symbols, но выполняет тело единожды для каждого символа каждого пакета. (Функция не обрабатывает все возможные символы, так как символ может быть доступен ни в одном пакете. Обычно, дезинтернированые символы не доступны ни в одном пакете.) В общем случае тело может выполняться для одно символа несколько раз, так как этот символ может встречаться в нескольких пакетах.

The clarification voted by X3J13 in March 1988 for do-symbols , regarding redundant executions of the body for the same symbol, applies also to do-all-symbols.

Аргумент package может быть как объектом пакета, так и его именем. Смотрите раздел 11.2.

Пользователь ограничен в создании побочных действий так, как это описано в разделе 7.9


[Макрос] with-package-iterator (mname package-list {symbol-type}+){form}*

Имя mname связывается и определяется как если бы с помощью macrolet, лексически для тела form, и является «генератором», таким образом, что каждый вызов (mname) будет возвращать следующий символ, из списка всех символов, которые присутствуют в пакетах, перечисленных в списке полученным от выражения package-list (которое вычисляется единожды).

Каждый элемент значения package-list может быть или объектом пакета или его именем. Кроме того, если значение package-list является просто объектом пакета или его и именем, то оно трактуется как список с одним этим элементом. Если значение package-list равно nil, то трактуется как пустой список.

При каждом вызове «генератора» возможны два исхода. Если символы для обработки ещё есть, возвращается четыре значения: t, символ, ключевой символ, который характеризует искомый символ (смотрите ниже) и пакет, в котором данный символ доступен. Если символом для обработки не осталось, то возвращается одно значение: nil.

Когда «генератор» во втором значении возвращает символ, четвёртое значение это всегда пакет из списка, который был получен в package-list, и третье значение характеризует искомый символ так: :internal означает, что символ внутренний; :external означает, что символ внешний (экспортированный); и :inherited означает, что символ не представлен (следовательно не скрыт), но унаследован из некоторого другого пакета, который используется в пакете из четвёртого значения.

Элементы symbol-type в вызове with-package-iterator не вычисляются. Их может быть несколько, порядок не имеет значения. Они указывают на свойства интересующих пользователя символов. Символ будет возвращён из «генератора» только если его свойство было указано в symbol-type. Стандартными значениями symbol-type являются :internal, :external, :inherited, но реализации могут добавлять свои свойства. В случае если в symbol-type не указано ни одно свойство, или если указанные не поддерживаются реализацией, сигнализируется ошибка.

Порядок, в котором возвращаются символы с помощью «генератора», никак не коррелирует с порядком перечисления пакетов в package-list. При наличии в package-list более одного пакета, символы, которые доступны более чем из одного пакета могут быть возвращены один или более раз. Даже если указан только один пакет, символы унаследованные различными путями могут быть обработаны один или несколько раз.

Неявное состояние перечисления символов имеет динамическую продолжительность жизни. После завершения формы with-package-iterator вызов «генератора» приведёт к ошибке.

Любое количество вызовов with-package-iterator и связанных с ними макросов может быть вложенным.

Пользователь ограничен в создании побочных действий так, как это описано в разделе 7.9 ____________________________________________________

Обоснование: Этот функция в некотором смысле более гибкий, чем do-symbols и подобные ей функции. В частности, он позволяет реализовать подвыражения для loop для итераций над символами пакетов и переносимо, и эффективно (смотрите главу 26).

___________________________________________________________________________________________________________