Глава 11
Пакеты

 11.1 Правила согласованности
 11.2 Имена пакетов
 11.3 Преобразование строк в символы
 11.4 Экспортирование и импортирование символов
 11.5 Конфликты имён
 11.6 Системные пакеты
 11.7 Функции и переменные для системы пакетов

В ранних реализациях Lisp’а для всех символов использовалось одно пространство имён. В больших Lisp’овых системах, с модулями, написанными разными программистами, случайные совпадения имён стали серьёзной проблемой. Common Lisp решает эту проблему с помощью системы пакетов, производной от системы пакетов, разработанной для Lisp Machine [55]. Кроме того система пакетов делает модульную структуру больших Lisp систем более явной.

Пакет — это структура данных, которая устанавливает связь между именами символов (строковый тип) и самими символами. Во время работы только один пакет является текущим, и этот пакет используется Lisp’овым считывателем для преобразовании строк-имён символов в сами символы. Текущий пакет храниться в глобальной переменной *package*. С помощью имени пакета в имени символа существует возможность ссылаться на символы других пакетов. Например, когда Common Lisp’ом будет прочтено имя foo:bar, то оно будет ссылаться на символ bar из пакета foo. (Но при условии, что bar является экспортированным символом из foo, то есть символом, который является видимым извне пакета foo. Ссылка на внутренний символ требует удваивания двоеточия: foo::bar.)

Связь символов и их имён, доступных в данном пакете, делится на два вида: внешняя и внутренняя. Речь будет идти о символах доступных с помощью этих связей, как о внешних или внутренних символах пакета, хотя на самом деле различаются связи, а не символы. Внутри заданного пакета имя ссылается максимум на один символ. Если оно ссылается на символ, тогда этот символ в данном пакете является или внешним, или внутренним.

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

Символ может встречаться во многих пакетах. Он будет всегда иметь одно и то же имя, но в некоторых пакетах он может быть внешним (если его экспортировали), а в других внутренним. С другой стороны, одно и то же имя символа (строка) может ссылаться на различные символы в различных пакетах.

Обычно, символ, который встречается в одном и более пакетах, будет иметь только один родительский пакет, называемый домашний пакет символа. Говорится, что такой пакет содержит данный символ. Все символы содержат свойство, называемое ячейка пакета, которое хранит указатель на домашний пакет. Символ, который имеет домашний пакет, называется пакетным. Некоторые символы не имеют домашнего пакета. Они называются беспакетными. Их ячейка пакета содержит значение nil.

Пакеты могут быть представлены как слои. С этой точки зрения, для пользователя пакет выглядит как коллекция связей имён с внутренними и внешними символами. Некоторые из этих связей могут устанавливаться внутри самого пакета. Другие связи наследуются из других пакетов с помощью конструкции use-package. (Механизм такого наследования описан ниже.) В дальнейшем, мы будем называть символ доступным в пакете, если на него можно сослаться без указания имени пакета, вне зависимости от того унаследована ли связь символов с именем или установлена текущим пакетом. И мы будем называть символ родным в пакете, если связь установлена самим пакетом, а не унаследована. Таким образом, родной символ в пакете является доступным, но доступный символ не обязательно является родным.

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

«Пакетировать символ!» означает сделать так, чтобы пакет стал владельцем символа, если до этого было не так. Этот процесс выполняется функцией intern. Если символ прежде был бесхозным (никому не принадлежал), тогда пакет становится его владельцем (домашним пакетом). Но если символ уже принадлежал кому-то, то домашний пакет не меняется.

«Лишить символ пакета» означает убрать его из пакета. Данный процесс выполняется функцией unintern. FIXME