В ранних реализациях Lisp’а для всех символов использовалось одно
пространство имён. В больших Lisp’овых системах, с модулями, написанными
разными программистами, случайные совпадения имён стали серьёзной
проблемой. Common Lisp решает эту проблему с помощью системы пакетов,
производной от системы пакетов, разработанной для Lisp Machine [55]. Кроме
того система пакетов делает модульную структуру больших Lisp систем более
явной.
Пакет
Связь символов и их имён, доступных в данном пакете, делится на два вида: внешняя и внутренняя. Речь будет идти о символах доступных с помощью этих связей, как о внешних или внутренних символах пакета, хотя на самом деле различаются связи, а не символы. Внутри заданного пакета имя ссылается максимум на один символ. Если оно ссылается на символ, тогда этот символ в данном пакете является или внешним, или внутренним.
Внешние символы являются частью интерфейса пакета, доступного для других пакетов. Внешние символы должны быть аккуратно спроектированы и предоставлены для пользователей этого пакета. Внутренние символы предусмотрены только для внутреннего использования, и эти символы обычно скрыты от других пакетов. Большинство символов создаются как внутренние. Они становятся внешними, только если явно передаются в команду export. Таким образом внешние символы это то же, что и экспортированные функции и переменные в других языках, поэтому внешние символы иногда будут называться экспортированными от имени функции export.
Символ может встречаться во многих пакетах. Он будет всегда иметь одно и то же имя, но в некоторых пакетах он может быть внешним (если его экспортировали), а в других внутренним. С другой стороны, одно и то же имя символа (строка) может ссылаться на различные символы в различных пакетах.
Обычно, символ, который встречается в одном и более пакетах, будет иметь только один родительский пакет, называемый домашний пакет символа. Говорится, что такой пакет содержит данный символ. Все символы содержат свойство, называемое ячейка пакета, которое хранит указатель на домашний пакет. Символ, который имеет домашний пакет, называется пакетным. Некоторые символы не имеют домашнего пакета. Они называются беспакетными. Их ячейка пакета содержит значение nil.
Пакеты могут быть представлены как слои. С этой точки зрения, для пользователя пакет выглядит как коллекция связей имён с внутренними и внешними символами. Некоторые из этих связей могут устанавливаться внутри самого пакета. Другие связи наследуются из других пакетов с помощью конструкции use-package. (Механизм такого наследования описан ниже.) В дальнейшем, мы будем называть символ доступным в пакете, если на него можно сослаться без указания имени пакета, вне зависимости от того унаследована ли связь символов с именем или установлена текущим пакетом. И мы будем называть символ родным в пакете, если связь установлена самим пакетом, а не унаследована. Таким образом, родной символ в пакете является доступным, но доступный символ не обязательно является родным.
Символ называется пакетным, если он доступен в этом пакете и имеет некоторый родительский пакет. Обычно все символы доступные в пакете будут в собственности некоторого пакета, но иногда описываются случаи доступности никому не принадлежащего (беспакетного) символа.
«Пакетировать символ!» означает сделать так, чтобы пакет стал владельцем символа, если до этого было не так. Этот процесс выполняется функцией intern. Если символ прежде был бесхозным (никому не принадлежал), тогда пакет становится его владельцем (домашним пакетом). Но если символ уже принадлежал кому-то, то домашний пакет не меняется.
«Лишить символ пакета» означает убрать его из пакета. Данный процесс выполняется функцией unintern. FIXME