- PVSM.RU - https://www.pvsm.ru -

По моим наблюдениям, минимум раз в неделю в списке c.l.l [1] или другом Lisp-списке «новички» [2] путаются в том, что связано с пакетами. Говорят о «загрузке» пакета, «требовании» (requiring) пакета, удивляются тому, что после загрузки системы нужно пользоваться маркерами пакетов [3] и т.д. Меня это раздражает, думаю также, что это может быть одной из причин, почему начинающие считают, что использование библиотек в Lisp сложнее, чем есть на самом деле.
Обычно я прекращаю попытки написать полезное объяснение, и, естественно, это объяснение очень простое. Я создал эту страницу, чтобы в следующий раз просто отправить сюда, вместо того, чтобы снова и снова объяснять одно и то же.
Прежде всего следует иметь ясную голову. Термин «пакет» сильно перегружен. В дистрибутивах Linux вроде Debian или Gentoo есть «пакеты», «пакеты» есть в языках программирования Java, Perl или Python. Вполне вероятно, что вы пришли в Lisp с предвзятым мнением относительно того, что такое «пакет» или чем он должен быть.
Пакетом в Common Lisp называется полноправный элемент языка, семантика которого четко определена стандартом. Более того, из всех обсуждаемых на этой странице терминов, этот — единственный, имеющий (в контексте Common Lisp) однозначное определение. Пакеты — это, строго говоря, контейнеры для символов. Можно сказать, что они нужны для помощи в организации отдельных пространств имен в ваших программах.
В Common Lisp есть функции и макросы для создания, изменения, исследования и удаления пакетов. Очень хорошее введение в пакеты (и символы) можно найти в главе 21 [4] великолепной книги Practical Common Lisp [5] Питера Сайбела. Определение термина находится в главе 11 [6] (онлайн-версии) стандарта ANSI Common Lisp specification [7].
В общем, про пакеты это всё. Говоря технически, вы не загружаете пакеты. Вы можете загрузить (с помощь LOAD [8]) код, который в свою очередь создаст пакет, и это существенное различие.
Кроме того, если ваш Lisp жалуется, что не может найти какой-то пакет, это означает, что пакета как Lisp-объекта нет в образе (т.е. FIND-PACKAGE [9] возвращает NIL), потому что его еще никто не создал. Это не означает, что Lisp-машина поискала в файловой системе и ничего не нашла. (Частая причина такой неудачи состоит в том, что события происходят в неправильном порядке. Об этом ниже.)
Системы, в отличие от пакетов, даже не упоминаются в стандарте [7]. Тем не менее, опытные Lisp-программисты знают этот термин, поскольку им потребуютется знать и применять какой-то инструмент определения систем. Наиболее заметный сегодня — ASDF [10] (используется большинством Lisp-библиотек с открытым исходным кодом); другой известный инструмент определения систем, гораздо старше ASDF — MK:DEFSYSTEM [11]. Некоторые разработчики также поставляют свои инструменты определения систем вместе с дистрибутивами, см. например, Common Defsystem [12] для LispWorks.
В этом ключе система, строго говоря, это набор кода плюс инструкция по его обработке, например, зависимости от других систем, что следует загрузить/скомпилировать в первую очередь и т.д. Другими словами, инструмент определения систем по своему назначению похож на make [13] или Ant [14].
Кроме того, инструмент определения систем обычно может намного больше — Common Defsystem может, например, интегрировать файлы библиотек типов COM [15], ASDF полностью расширяем и использовался, среди прочего, для компиляции файлов на C [16]. Он также часто используется для определения тестовых наборов [17] описываемой системы.
Хотя ASDF и весьма популярен, он не вездесущ. Он идет предустановленным со многими Lisp-системами вроде SBCL, OpenMCL или AllegroCL, вероятнее всего, что он загрузится и в других Lisp-системах, но этот факт не делает его частью Common Lisp. Это набор кода без явной спецификации и с разными версиями, которые бывают несовместимы между собой.
Поди пойми…
Стандарт [7] определяет модули лишь поверхностно. Есть две вещи, которые нужно знать о REQUIRE [18], PROVIDE [18] и *MODULES* [19] — эта функциональность не рекомендуется (deprecated) и зависит от реализации. Пусть вас не беспокоит тот факт, что эта функциональность не рекомендуется. Все дистрибутивы сегодня содержат указанные функции, и вероятность того, что появится новый стандарт ANSI и все реализации внезапно уберут их, конечно, мала. Вот о чем стоит беспокоиться, так это о том, что REQUIRE может быть удобным, но не переносимым методом (если вас, конечно, беспокоят механизмы переносимости).
Например, в LispWorks можно использовать
(require "foreign-parser")
для загрузки парсера, способного читать определения на C [20], но это не сработает на OpenMCL. Также можно вызвать
(require :asdf)
для загрузки ASDF на OpenMCL, но не в LispWorks.
Некоторые дистрибутивы предлагают хуки для настройки работы REQUIRE, и существуют расширения вроде common-lisp-controller [21], соединяющие REQUIRE с ASDF, однако в общем случае модуль — это такая штуковина, которая зависит от реализации и которую не следует путать с системами (ASDF), и, тем более, с пакетами.
Скорее всего вы не найдете четкого определения, что такое библиотека. Большинство людей думают об этом как о коллекции кода, предназначенного для выполнения одной или нескольких определенных задач и распространяемого как единое целое, обычно в виде сжатого архива, который можно откуда-то скачать. На самом деле, это неясное определение является, думаю, наиболее подходящим при разговоре о программах, написанных на Lisp. Большинство Lisp-библиотек сегодня включают в себя определение (ASDF) системы, но это вовсе не обязательно. Возможно, в зависимости от способа получения, это будет модуль в вашей Lisp-системе, но и это тоже не обязательно. Кроме того, библиотека обычно определяет один или несколько пакетов, а может и не определять ни одного.
И, по соглашению, а может из-за недостатка фантазии, может сложится и часто складывается ситуация, когда библиотека «Ку» идет с определением системы «Ку», которую можно загрузить как модуль «Ку». После загрузки кода получите новый пакет, называемый «Ку». Четыре разных сущности с одинаковым именем! Я допускаю, что это сбивает с толку, но надеюсь, что несколько предыдущих абзацев помогли слегка прояснить ситуацию.
Часто люди жалуются, что они не могут скомпилировать файл, содержащий код вроде этого:
;; в этой строчка также может быть написано (require :cl-ppcre)
(asdf:oos 'asdf:load-op :cl-ppcre)
(defun my-simple-number-scanner (string)
(cl-ppcre:scan "^[0-9]+$" string))
Почему так? Почему я могу загрузить этот файл, но не могу скомпилировать его? И почему я могу скомпилировать его после загрузки? Не странно ли?
Нет, не странно. Компилятор читает первую форму (которая является инструкцией скомпилировать — если необходимо — и загрузить систему CL-PPCRE, но не выполнить ее. В конце концов, компилятор заинтересован лишь в компиляции кода. После выполнения первой формы он переходит ко второй форме, к определению функции. Здесь возможно сообщение об ошибке, так как Lisp-сканер, пытающийся читать эту форму, обнаружит последовательность символов «cl-ppcre:scan», которая должна обозначать внешний символ [22] из пакета CL-PPCRE, но самого пакета CL-PPCRE еще нет. В процессе загрузки системы CL-PPCRE, кроме всего прочего, создается пакет CL-PPCRE, но этого еще не произошло. Читайте главу 3 [23] CLHS.
Можно воспользоваться EVAL-WHEN [24] для указания компилятору загрузить CL-PPCRE перед чтением второй формы. Следует, однако, найти другой способ организации своего кода. Первая форма — это просто обявление того, что ваш код зависит от системы CL-PPCRE. Такое не должно находиться в том же файле, что и Lisp-код. Напишите определение системы [25] для вашей программы и поместите зависимости туда.
Автор: DmitrySolomennikov
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/novichok/10273
Ссылки в тексте:
[1] c.l.l: http://groups.google.com/group/comp.lang.lisp
[2] «новички»: http://ru.wikipedia.org/wiki/%D0%9D%D0%BE%D0%B2%D0%B8%D1%87%D0%BE%D0%BA
[3] маркерами пакетов: http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_p.htm#package_marker
[4] главе 21: http://lisper.ru/pcl/programming-in-the-large-packages-and-symbols
[5] Practical Common Lisp: http://lisper.ru/pcl/
[6] главе 11: http://www.lispworks.com/documentation/HyperSpec/Body/11_.htm
[7] ANSI Common Lisp specification: http://www.lispworks.com/documentation/common-lisp.html
[8] LOAD: http://www.lispworks.com/documentation/HyperSpec/Body/f_load.htm
[9] FIND-PACKAGE: http://www.lispworks.com/documentation/HyperSpec/Body/f_find_p.htm
[10] ASDF: http://www.cliki.net/asdf
[11] MK:DEFSYSTEM: http://www.cliki.net/mk-defsystem
[12] Common Defsystem: http://www.lispworks.com/documentation/lw50/LWUG/html/lwuser-195.htm
[13] make: http://ru.wikipedia.org/wiki/Make
[14] Ant: http://ru.wikipedia.org/wiki/Apache_Ant
[15] интегрировать файлы библиотек типов COM: http://www.lispworks.com/documentation/lw50/COM/html/com-131.htm
[16] компиляции файлов на C: http://git.b9.com/cgi-bin/gitweb.cgi?p=clsql.git;a=blob_plain;f=clsql-uffi.asd;hb=master
[17] определения тестовых наборов: http://weitz.de/odd-streams/#download
[18] REQUIRE: http://www.lispworks.com/documentation/HyperSpec/Body/f_provid.htm
[19] *MODULES*: http://www.lispworks.com/documentation/HyperSpec/Body/v_module.htm
[20] парсера, способного читать определения на C: http://www.lispworks.com/documentation/lw50/FLI/html/fli-167.htm
[21] common-lisp-controller: http://www.cliki.net/common-lisp-controller
[22] внешний символ: http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_e.htm#external_symbol
[23] главу 3: http://www.lispworks.com/documentation/HyperSpec/Body/03_.htm
[24] EVAL-WHEN: http://www.lispworks.com/documentation/HyperSpec/Body/s_eval_w.htm
[25] Напишите определение системы: http://weitz.de/starter-pack/#own
Нажмите здесь для печати.