Регистрация | Войти
Lisp — программируемый язык программирования
RSS
Перевод статьи про пакеты.
dmitrys99 - 26.06.2012 20:24, Сообщений - 12
Перевел статью Edi Weitz про пакеты. Перевод здесь.

Мне объяснение показалось очень полезным.

Дмитрий.
[#]
Переводы это хорошо, будет куда посылать новичков:)
LinkFly - 26.06.2012 21:19
[#]
В CLTL2 очень хорошее обьяснение.
lithp - 27.06.2012 05:10
[#] Ответ на комментарий от lithp 27.06.2012 05:10
Отлично, спасибо. А может ещё на тутошнюю вики?

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

maxidler с хабра несколько прав. Всё богатство красок кл сразу лучше показывать, так как никто не верит или говорит, что не нужно.
michael.filonenko - 28.06.2012 00:17
[#] Ответ на комментарий от michael.filonenko 28.06.2012 00:17
Статья не устарела в смысле описания сущностей и различий между ними.

Про Quicklisp прошу уточнить, о чем идет речь: о том, чтобы опубликоваться на quicklisp.org или о том, чтобы грузить систему с помощью (ql:quickload ...)? Если о первом, то я не знаю, как это делается :)

А если о втором, то да, это можно расписать, правда, пока непонятно, где для этого время выкроить.
Насчет wiki, ну да, наверное надо разместить. Опять же, время появится, опубликую.
dmitrys99 - 28.06.2012 09:48
[#] Ответ на комментарий от dmitrys99 28.06.2012 09:48
Вот жеш. В прошлый раз хотел сказать, что всё богатство коммон лиспа лучше сразу НЕ показывать.

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

Касаемо всей статьи, я имеел ввиду, что для маркетинговых целей, а тем более для хабраудитории, было бы неплохо что-то вроде.

Итак у вас есть всякие переменные и функции в репле или лисп файле:

(defvar *who* "world")

(defun greetings ()
  (format t "Hello, ~a" *who*)
)
а) Заверните это всё хозяйство в пространство имён. В коммон лиспе это называется пакет. Далее встречая слово пакет нужно думать о пространстве имён. Итак:
1. Создайте, если ещё не создан файл utils.lisp.

2. Сперва определите пакет с помощью defpackage. Импортируйте в него стандартный пакет common-lisp. Экспортируйте из него переменные и функции. Кстати в коммон лиспах переменные и функции адресуются с помощью символов. В нашем случае должно получится так:

(defpackage utils
  (:use common-lisp)
  (:export *who* greetings)
)

3. Затем перейдите в этот пакет командой in-package:

(in-package utils)

4. А затем уже разместите всё своё хозяйство:

(defvar *who* "world")

(defun greetings ()
  (format t "Hello, ~a" *who*)
)
б) Теперь необходимо сообщить коммон лиспу о том, как всё это загрузить. Есть много способов, бла-бла-бла. Единственно мейнстримный asdf. Все его используют, все довольны. Следующий шаг создание пакета (в коммон лиспе это называется asdf система). Теперь всегда встречая слово система, думайте о готовой к использованию библиотеке:
1. Для этого создаётся файл utils.asd, и в нём с помощью макроса asdf:defsystem перечисляются коммон лисповые системы:

(asdf:defsystem utils
  :licence "MIT"
  :version "1.0.0"
  :author "Your Name <yourname@email.com>"
  :depends-on ()
  :components ((:file "utils.lisp"))
  :description "Small library."
)

Как вы заметили в этот макрос передаются всякие списки. Первый важный :depends-on содержит другие asdf-cистемы, от которых зависит данная система. Все живое в мире друг от друга зависит, также и коммон лисповые системы могут друг от друга зависеть. Этот список для библиотеки cl-portaudio, которая позволяет играть/записывать звук в коммон лиспе выглядит так:

:depends-on (:cffi :ffa)

Далее идёт еще более хитрый параметр :components. В нем перечисляются а) модули (или же просто группы файлов, которые, например в одной папке лежат) б) эти самые файлы в) кого за кем грузить (или порядок загрузки файлов, или же зависимости между файлами). с) ещё пару параметров, сейчас их знать необязательно. Например, для cl-portaudio этот параметр выглядит так:

:components ((:module src
                :serial t
                :components (
                             (:file "package")
                             (:file "portaudio")
)
)
)

:module src означает, что файлы объеденены в модуль, и по-умолчанию находятся в одноимённой папке.
:serial t означает, что файлы зависимы друг от друга в том порядке, в котором перечислены.

в) Вуаля, библиотека на коммон лиспе готова. Допустим это папка myutils, c файлами utils.asd, utils.lisp. Теперь зайлейте это всё на github, ну или вообще на любой хостинг.

г) В коммон лиспе стараниями Зака Бина появлися прекрасный менеджер библиотек, который позволяет одной командой скачать загрузить как саму библиотеку, так и все её зависимости. Например,

(mapcar #'quicklisp:quickload '(:cl-gtk2-gtk :cl-portaudio))

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

Для того, чтобы ваша библиотека попала в репозитарий quicklisp'а, просто оставьте на неё ссылку здесь: https://github.com/quicklisp/quicklisp-projects/issues?direction=desc&sort=created&state=open. Внимание, соблюдайте традицию вежливости, начинайте заголовок со слова "Please"!:)


Теперь ещё большее внимание!!! Всё это можно было проделать за ДВЕ команды. Итак:

(ql:quickload :quickproject)

(quickproject:make-project "path/to/library" :depends-on '() :license "MIT" :author "Your name <your@email.com>" :name "utils")


michael.filonenko - 28.06.2012 12:27
[#] Ответ на комментарий от michael.filonenko 28.06.2012 12:27
Ах жеш, оно оказывается наклонный шрифт делало.
michael.filonenko - 28.06.2012 12:28
[#] Ответ на комментарий от michael.filonenko 28.06.2012 12:27
Я джва года ждал такой туториал.
andy128k - 28.06.2012 14:57
[#] Ответ на комментарий от michael.filonenko 28.06.2012 12:27

(mapcar #'quicklisp:quickload '(:cl-gtk2-gtk :cl-portaudio))
эквивалентно

(ql:quickload '(:cl-gtk2-gtk :cl-portaudio))

но последнее короче.
motopeh - 28.06.2012 19:10
[#] Ответ на комментарий от motopeh 28.06.2012 19:10
Моя ошибка.
michael.filonenko - 28.06.2012 20:19
[#] Ответ на комментарий от michael.filonenko 28.06.2012 12:27
создал проект таким образом(как вы писали):
(ql:quickload :quickproject)

(quickproject:make-project "path/to/library" :depends-on '() :license "MIT" :author "Your name <your@email.com>" :name "utils")
загрузил:

(ql:quickload "library")
все работало

но после перезагрузки РЕПЛа библиотека перестала загружаться(говорит, что такой не знает).

stairs - 01.08.2012 13:42
[#] Ответ на комментарий от stairs 01.08.2012 13:42
меня лучше на ты.

надо либо
(pushnew "/path/to/library/" asdf:*central-registry*)

, либо сделать сделать в папке ~/quicklisp/local-prjects ссылку на path/to/library.
michael.filonenko - 01.08.2012 15:52
[#] Ответ на комментарий от michael.filonenko 01.08.2012 15:52
По-моему, статья не особо. Два наиболее существенных объяснения начинаются с тавтологий. Автор вряд ли ставил цель что-то объяснить, скорее, запутать. Немного подправил, может быть, будет лучше.

По моим наблюдениям, минимум раз в неделю в списке <a href="http://groups.google.com/group/comp.lang.lisp">c.l.l</a> или другом Lisp-списке <a href="http://ru.wikipedia.org/wiki/%D0%9D%D0%BE%D0%B2%D0%B8%D1%87%D0%BE%D0%BA">«новички»</a> путаются в том, что связано с пакетами. Говорят о «загрузке» пакета, «требовании» (requiring) пакета, удивляются тому, что после загрузки системы нужно пользоваться <a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_p.htm#package_marker">маркерами пакетов</a> и т.д. Это может быть одной из причин, почему начинающие считают, что использование библиотек в Lisp сложнее, чем есть на самом деле.<br/>
<a name="habracut"></a><br/>
Прежде всего следует учесть, что термин «пакет» сильно перегружен. В дистрибутивах Linux вроде Debian или Gentoo есть «пакеты», «пакеты» есть в языках программирования Java, Perl или Python. Вполне вероятно, что вы пришли в Lisp с предвзятым мнением относительно того, что такое «пакет» или чем он должен быть.<br/>
<br/>
<h4>Пакеты</h4><br/>
Чтобы разобраться в пакетах, нужно сначала разобраться в символах. Что такое символ? Символ - это, грубо говоря, идентификатор. В большинстве языков программирования идентификатор что-то обозначает (функцию, переменную или класс). В лиспе символ существует "сам по себе" и может ничего не означать, а использоваться как самостоятельная именованная сущность (объект типа SYMBOL).
<br/>
Пакет в лиспе - это не более чем таблица, включающая в себя ссылки на какие-то символы. В других языках тоже есть таблицы символов, но они существуют, как правило, только во время компиляции. В них заносятся все правильные определения, обнаруженные компилятором. Встречая ссылку на имя, компилятор или интерпретатор языка программирования ищет это имя в таблице символов, и делает вывод о том, правомерно ли использование имени в данном случае. Если это нужно, новое имя добавляется в таблицу. Если компилятор обрабатывает несколько файлов, для каждого из них строится своя таблица символов. Поэтому, имя, допустимое в одном файле, может быть недопустимым в другом, в зависимости от разного рода объявлений import, #include, use и т.п. После компиляции таблица символов больше не нужна и удаляется. Пакет в лиспе - это и есть аналог такой таблицы символов.
<br/>
В лиспе при компиляции файла происходит примерно то же самое, что и в других языках. Особенность лиспа состоит в том, что пакет, как правило, существует не только во время компиляции, но и во время исполнения. Пакет, если он существует, всегда доступен пользователю для таких операций, как добавление или удаление символа или получение перечня всех символов.
<br/>
Пользователь сталкивается с пакетами сразу при начале работы с лисп-средой, когда вводит команды в подсказке интерпретатора. Первым делом среда осуществляет чтение формы. Чтение происходит в контексте текущего пакета (переменная *PACKAGE*). При этом, каждый обнаруженный символ без префикса пакета ищется в текущем пакете и, если символа нет, то он сразу же добавляется. Для поиска символа в другом пакете нужно задать имя этого пакета через одно или два двоеточие, например, other-package::name. Интерпретация символа как имени функции, переменной, класса или другой сущности происходит позже, на этапе компиляции или выполнения.
<br/>
Пакеты могут ссылаться друг на друга, с помощью слова :use в форме defpackage, тогда символы, экспортируемые из используемого пакета, будут в используещем.
<br/>
Символ может быть динамически добавлен в пакет или удалён из него.
<br/>
Таким образом, пакет имеет некоторые черты свойства модуля или пространства имён, но в нём обособлена функция ведения перечня имён.
<br/>
В Common Lisp есть функции и макросы для создания, изменения, исследования и удаления пакетов. Очень хорошее введение в пакеты (и символы) можно найти в <a href="http://lisper.ru/pcl/programming-in-the-large-packages-and-symbols">главе 21</a> великолепной книги <a href="http://lisper.ru/pcl/">Practical Common Lisp</a> Питера Сайбела. Определение термина находится в <a href="http://www.lispworks.com/documentation/HyperSpec/Body/11_.htm">главе 11</a> (онлайн-версии) стандарта <a href="http://www.lispworks.com/documentation/common-lisp.html">ANSI Common Lisp specification</a>.<br/>
<br/>
В общем, про пакеты это всё. Говоря технически, вы не <i>загружаете</i> пакеты. Вы можете загрузить (с помощь <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_load.htm"><code>LOAD</code></a>) код, который в свою очередь создаст пакет (с помощью DEFPACKAGE), и это - существенное различие.<br/>
<br/>
Кроме того, если ваш Lisp жалуется, что не может найти какой-то пакет, это означает, что пакета как Lisp-объекта нет в образе (т.е. <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_find_p.htm"><code>FIND-PACKAGE</code></a> возвращает <code>NIL</code>), потому что его еще никто не создал. Это не означает, что Lisp-машина поискала в файловой системе и ничего не нашла. (Частая причина такой неудачи состоит в том, что события происходят в неправильном порядке. Об этом ниже.)<br/>
<br/>
<h4>Системы</h4><br/>
Системы, в отличие от пакетов, даже не упоминаются в <a href="http://www.lispworks.com/documentation/common-lisp.html">стандарте</a>.
Системы управляют сборкой программы. Ближайшие аналоги системы - это makefile для <a href="http://ru.wikipedia.org/wiki/Make">make</a> или файлы проекта в различных IDE. Файл определения системы задаёт перечень файлов, нужных для загрузки системы, порядок и действия для загрузки каждого файла, а также, зависимость данной системы от других систем. Наиболее заметный на сегодня инструмент определения систем — <a href="http://www.cliki.net/asdf">ASDF</a> (используется большинством Lisp-библиотек с открытым исходным кодом); другой известный инструмент определения систем, гораздо старше ASDF — <a href="http://www.cliki.net/mk-defsystem">MK:DEFSYSTEM</a>. Некоторые разработчики также поставляют свои инструменты определения систем вместе с дистрибутивами, см. например, <a href="http://www.lispworks.com/documentation/lw50/LWUG/html/lwuser-195.htm">Common Defsystem</a> для LispWorks.<br/>
<br/>
Кроме того, инструмент определения систем обычно может намного больше — Common Defsystem может, например, <a href="http://www.lispworks.com/documentation/lw50/COM/html/com-131.htm">интегрировать файлы библиотек типов COM</a>, ASDF полностью расширяем и использовался, среди прочего, для <a href="http://git.b9.com/cgi-bin/gitweb.cgi?p=clsql.git;a=blob_plain;f=clsql-uffi.asd;hb=master">компиляции файлов на C</a>. Он также часто используется для <a href="http://weitz.de/odd-streams/#download">определения тестовых наборов</a> описываемой системы.<br/>
<br/>
Хотя ASDF и весьма популярен, он не вездесущ. Он идет предустановленным со многими Lisp-системами вроде SBCL, OpenMCL или AllegroCL, вероятнее всего, что он загрузится и в других Lisp-системах, но этот факт не делает его частью Common Lisp. Это - набор кода без явной спецификации и с разными версиями, которые бывают несовместимы между собой.<br/>
Поди пойми…<br/>



den73 - 17.12.2012 17:40
@2009-2013 lisper.ru