четверг, 25 декабря 2008 г.

sbcl-slime, clisp-slime and etc

Хотелось бы поделиться двумя советами, которые представляются мне довольно важными при использовании Slime (а если вы работаете с Common Lisp, то почти наверняка используете именно его).

Как известно, какая именно реализация CL будет запущена при запуске Slime определяется переменной inferior-lisp-program. Однако глобальное определение этой переменной и использование команды slime довольно неудобно, поскольку не позволяет легко запускать различные реализации CL. Я же часто запускаю как sbcl, так и clisp, а в планах поработать и с другими реализациями. Не переопределять же эту переменную каждый раз, когда хочешь поработать с другой реализацией? Поэтому, в свой конфиг Emacs я добавил следующее:
(defmacro lisp-slime (lisp path &optional coding)
(let ((funname (intern (format "%s-slime" lisp))))
`(defun ,funname ()
(interactive)
(let ((inferior-lisp-program ,path)
(slime-net-coding-system (or ,coding 'utf-8-unix)))
(slime)))))

(lisp-slime sbcl "/usr/bin/sbcl")
(lisp-slime clisp "/usr/bin/clisp")
Теперь у меня доступны команды sbcl-slime и clisp-slime, которые позволяют легко запустить sbcl или clisp соответственно. Ну а макрос lisp-slime может быть использован для лёгкого добавления других реализаций.

Второй совет связан с использование hyperspec. Функция hyperspec-lookup отрывает документацию по символу в браузере и очень удобно, если это emacs-w3m. Для того, что бы так и происходило (в моей системе по умолчанию запускается Firefox), можно просто переопределить функцию browse-url-default-browser таким образом, что бы она вызывала w3m-browser-url. Однако, в таком случае функция browser-url всегда будет открывать указанный url в emacs-w3m, чего бы мне хотелось избежать (w3m хорош для работы с hyperspec, но для других задач я предпочитаю использовать Firefox). Добиться того, что бы hyperspec открывался в emacs-w3m (в том числе, при использовании slime-hyperspec-lookup), а другие url в браузере по умолчанию (в моём случае в Firefox) можно следующим образом:
(require 'hyperspec)

(defun hyperspec-lookup (&optional symbol-name)
(interactive)
(let ((browse-url-browser-function 'w3m-browse-url))
(if symbol-name
(common-lisp-hyperspec symbol-name)
(call-interactively 'common-lisp-hyperspec))))
Примечание: хотя в моей системе hyperspec загружается автоматически, мне всё же пришлось добавить (require 'hyperspec) до определения hyperspec-lookup, что бы гаранитровать правильный порядок инициализации.

среда, 24 декабря 2008 г.

Переход на git

Начал эксперимент, по переводу одного из внутренних проектов на git, если всё пройдёт успешно (а думаю, что так оно и будет), то буду переводить все свои проекты с subversion на git.

вторник, 23 декабря 2008 г.

garbage-pools-0.1.1

Добавил в garbage-pools одну функцию: cancel-object-cleanup, которая позволяет отменить регистрацию ранее зарегистрированного в пуле объетка. Это приём в стиле C++ : вообще-то, удалять ресурс не собираемся, но в случае возникновения исключения он должен быть удалён, поэтому готовим его к удалению, а в после успешного прохождения опасной зоны спасаем и используем по назначению.

Поскольку успел заюзать эту функцию в cl-libxml2, то в связи с её новым релизом, пришлось строчо выпустить garbage-pools версии 0.1.1 (чуть не забыл это сделать).

cl-libxml2-0.2.5

Выпустил cl-libxml2 версии 0.2.5. В этой версии:
  1. Появилась обработка ошибок: теперь все ошибки, генерируемые libxml2, транслируются в conditions или warnings (для нефатальных ошибок).
  2. Кроме того, понаводил порядка в исходниках, что вылилось в достаточно заметное изменение их структуры.
  3. В результате выполнения предыдущих двух пунктов вылезло на свет некоторое колличество багов, которые и были исправлены.
Как личное достижение могу отметить то, что на данный момент ознакомился с исходным кодом libxml2 процентов на 30% - документации по библиотеке, я бы сказал, не вполне вменяема, пока код не посмотришь - ничего толком не поймёшь. Так же обнаружил, что в некоторых местах html-документация "не совсем" соответствует комментариям в исходниках, что немного насторожило...

А тем временем, в gentoo-lisp-overlay появилась Xuriella XSLT, а ещё немного ранее уже был добавлен Plexippus XPath. Появись это хотя бы на полгода раньше, то вероятно начинать cl-libxml2 я бы не стал. Однако, теперь забрасывать этот проект не при каких обстоятельствах не собираюсь: полноценная поддержка xml критически важна для меня, а упомянутые проекты ещё сыроваты (им ведь в самом деле приходится писать реализация, а я только обёртку делаю для давно и хорошо известной библиотекой).

четверг, 18 декабря 2008 г.

"Ни дня без строчки" или cl-routes-0.1.5

Выпустил cl-routes версии 0.1.5, в состав пакета включил hunchentoot-routes.asd, который позволяет производить маршрутизацию не только по структуре url, но также по хосту (очень люблю vhost в apache) и методу запроса, типа:
(routes.h:connect-handler *map*
"index.html"
'handler-for-post-query-on-index.html
:host "archimag:8080"
:method :post)
Конечно, routes.asdf всё это тоже поддерживает, но нужно понимать как он работает, что бы этим воспользоваться, а тут всё довольно прозрачно.

На мой взгляд, довольно неплохо для начала.

Тут, правда, возник другой насущный вопрос: где бы мне раздобыть ntlm-аутенфикацию для hunchentoot? Пропускать его (hunchentoot) через apache не хочу. Что, опять писать самому?

вторник, 16 декабря 2008 г.

wget and sed всех победят!

Я вот не понимаю, зачем люди выкладывают книги в веб в html и при этом не дают возможности скачать архив? Что бы отнять у заинтересовавшегося пять минут времени? Захотел вот книжку скачать, Practical PostgreSQL, а архива нет, по крайней мере, не нашёл.

Ну да впрочем, искал я недолго:
wget -m -np http://www.commandprompt.com/ppbook/
Отлично, открываю - а там все ссылки абсолютные, типа /ppbook/p206. Нет думаю, нас так просто не возьмёшь:
sed -i -e "s/\/ppbook\///" *[[:digit:]]**
(такая маска нужна, что бы каталог images пропустить, а так там всё с цифрами)

Докачал css-файлы и изменил ссылки на них.

Кроме того, мне не понравились , что верхние части страниц занимает всякий мусор с http://www.commandprompt.com/, там ссылки на Home, About, поиск какой-то, короче, мне совершенно не нужные вещи. Решил победить:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:html="http://www.w3.org/1999/xhtml"
version="1.0">

<xsl:output method="html" public="-//W3C//DTD XHTML 1.0 Transitional//EN" system="DTD/xhtml1-transitional.dtd">

<xsl:template match="/">
<xsl:copy>
<xsl:copy-of select="@*">
<xsl:apply-templates select="*">
</xsl:copy>
</xsl:template>

<xsl:template match="div[@id='widetop']">
<xsl:template match="ul[@id='greymenu_wide']">

<xsl:template match="*|text()">
<xsl:copy>
<xsl:copy-of select="@*">
<xsl:apply-templates select="*|text()">
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Это xslt-преобразование копирует входящий html документ, вырезая из него блоки <div id="widetop"> и <ul id="greymenu_wide">
Плюс потребовался небольшой скрипт:
for path in $@
do
name=`echo $path | sed -e 's/ppbook\///'`
`xsltproc --html --novalid ppbook.xsl ppbook/$name > ppbook-copy/$name`
echo $name
done
Который был вызван следующим образом:
 ./ppbook.sh ppbook/*[[:digit:]]** 
И всё отлично работает. Но полчаса времени я потерял. Вот и спрашивается, зачем людям жизнь осложнять?

воскресенье, 14 декабря 2008 г.

cl-routes-0.1

Выпустил cl-routes версии 0.1. Оказалось, что использование механизма унификации делает решение проблемы почти тривиальным. Я немного в шоке... В исходниках и на cliki имеется небольшой пример использования совместно с hunchentoot. Конечно, нужно будет ещё много дорабатывать, но это уже после получения опыта практического применения.

четверг, 11 декабря 2008 г.

cl-routes

Начал работу над cl-routes (по ссылке можно не ходить, там ещё ничего интересного)
Routes tackles an interesting problem that comes up frequently in web development, how do you map a URL to your code? While there are many solutions to this problem, that range from using the URL paths as an object publishing hierarchy, to regular expression matching; Routes goes a slightly different way.
(взято отсюда)

Если совсем вкратце, то это (будет) Common Lisp реализация Rails routes system (с этой ссылкой постоянно какие-то проблемы), но поскольку в Ruby, и тем более в Rails, я соображаю чуть менее, чем ничего, то ориентируюсь на Python-реализацию этой идеи.

Правда, упомянутые реализации делают упор на использовании в рамках шаблонна MVC, который (как впрочем и вообще паттерны) я, мягко говоря, недолюбливаю, так что, никаких намёков на MVC в cl-routes не будет.

Основное предназначение cl-routes это имея набор шаблонов типа:
/forum/:chapter/:topic/:message
/forum/archives/:year/:month/:day
и т.д.
из переданного URL, например, /archives/2008/12/11 извлечь информацию о year (2008), month (12) и day (11), а также дополнительную информацию, указанную при регистрации шаблона (например, функцию обработчик, или ещё что).

Реализация routes мне не понравилась (а ведь первой мыслью было просто тупо портировать на CL). Во-первых, слишком много буков. Во-вторых, она основана на регулярных выражениях, что требует в худшем случае полной проверки всех шаблонов и в случае сервера приложений, на котором работает много-много мини-приложений может привести к заметным издержкам. Вообще, разбиением URL на части (например, с помощью puri) данная проблема тривиальным образом сводится к задаче унификации:
(UNIFY ("forum" ?year ?month ?day)
("forum" "2008" "12" "11"))
Это позволяет отказаться от использования регулярных выражений и заметно упростить решение. В голову сразу пришло применить cl-unification. Однако, использование этой библиотеки в авторском виде приводит к необходимости, как и в случае использования регулярных выражений, просматривать все зарегистрированные шаблоны в поисках нужного. Но ведь в большинстве случаев структура сайта образует дерево, а поиск в дереве должен быть гораздо быстрее. Эта дилемма останавливала меня от начала работы: с одной стороны очень хотелось применить унификацию, а с другой, не менее сильно хотелось вести поиск в дереве, а не в линейном списке. Наконец меня осенило: если нельзя сделать дерево из шаблонов, то ведь можно сделать шаблон в виде дерева! Правда, для этого необходимо добавить к cl-unification or-template, который бы давал возможность включать в один шаблон несколько различных вариантов. Например:
#T("forum" #T(or #T("archives" ?year ?mount ?day)
#T(?chapter ?topic ?message)))
Последовательное рекурсивное применение or-template даёт возможность построить дерево произвольной сложности. Кроме того, необходимо добавить ещё несколько других шаблонов, например, concat-template для унификации шаблонов содержащих что-нибудь типа ":{id1}_:{id2}.html".

В итоге я решился пропатчить необходимым образом cl-unification и попробовать уговорить автора принять эти изменения. Начал писать около 12 ночи и около 3 сильно раздражённый сделал перерыв на сон: что-то с этой библиотекой не так... Кроме того, проснувшись я обнаружил, что в некоторых случаях она просто неверно работает, по крайней мере, с точки зрения унификации. Это стало последней каплей, показавшей бесперспективность ориентации на cl-unification. Решил сделать свое решение. Ну не совсем своё. Взял код Питера Норвинга из AIMA, немного разбавил его парой светлых идей из cl-unification (кои там всё же есть) и включил это дело в cl-routes. Попутно обнаружил, что автор cl-unification также пытался "творчески" переработать код Питера Норвинга, но вот получилось у него не очень...

Вот, надеюсь, что первый релиз cl-routes выпущу ещё до Нового Года.

среда, 3 декабря 2008 г.

вспоминая postgres

Сегодня с утра надо было сделать небольшую базу данных. Давненько ничего подобного не делал... Запустил pgadmin3, потылкался минут 20 - не могу, раздражает, какие-то окошечки, выпадающие списки и прочие "рюшечки". А ведь вроде когда-то под виндой юзал более-менее нормально. Ладно, плюнул, M-x sql-postgres и уже через пять минут почувствовал себя человеком :-) Ну а psql оказалась очень даже вменяемой утилитой. Теперь думаю только так и работать.

вторник, 2 декабря 2008 г.

Могучий, могучий XSLT :-)

Вот уже не первый год активно пишу на XSLT, а до сих пор регулярно обнаруживаю для себя новые возможности. Вот и сегодня, столкнулся с не вполне очевидной проблемой, пусть есть такой документ:
<data>
<item-1>value-1</item-1>
<item-5>value-5></item-5>
<item-3>value-3</item-3>
....
<item-n>value-n</item-n>
</data>
Теперь надо в xslt получать значения этих узлов item-x по номеру (то бишь, по x). На первый взгляд показалось, что без dyn:evaluate из exslt не обойтись, правда такое решение на больших документах (ну там несколько тысяч таких записей), кроме того, ещё и тормозить будет заметно. Этот факт настолько меня растроил, что я тут же придумал решение:
<xsl:key name="item-by-number" match="/data/node()" use="substring(local-name(.), 6)">
и уже в самом коде можно так:
<xsl:value-of select="key('item-by-number', 3)">
Работает... к моему немалому удивлению и восторгу...

понедельник, 1 декабря 2008 г.

cl-libxml2 и exslt

Раздумываю над тем, чтобы реализовать exslt с помощью Common Lisp и cl-libxml2, ибо:
  • это даст возможность отладить соответствующие возможности cl-libxml2
  • libexslt не реализует многие полезные функции
Но, есть некоторые сомнения насчёт того, хватит ли мне желания/времени заниматься этим, ведь руки чешутся заняться некоторыми другими проектами, в частности, написать на CL подобие routes (что, в свою очередь, является Python-реализацией Rails routes system): а это последнее, что отделяет меня от непосредственной работы над сервером-приложений, который я уже несколько раз упоминал.

P.S. В последнее время решительно не хватает времени, из-за этого часто трачу его впустую, шарахаясь из стороны в сторону.

четверг, 27 ноября 2008 г.

cl-libxml2-0.2.1

Выпустил небольшой релиз cl-libxml2-0.2.1, сделал страницу с примерами кода и все таки, наконец, завел отдельную страницу на cliki: http://www.cliki.net/cl-libxml2.

пятница, 21 ноября 2008 г.

cl-libxml2-0.2

Сегодня выпустил cl-libxml2 версии 0.2

Основное внимание при разработке этого релиза было уделено возможностям расширения, это:
  • Custom URL resolve

  • XPath extension functions

  • XSLT extension elements

Custom URL resolve

Позволяет подсовывать парсеру, когда он желает загрузить какие-то внешние данные, свои данные. Это может потребоваться для реализации какого-то нестандартного протокола, либо, например, для поддержки авторизации, ну да много для чего. Выглядит это примерно так:
CL-USER> (in-package #:libxml2.tree)
#<PACKAGE "LIBXML2.TREE">
TREE> (defun my-resolve-1 (url id ctxt)
(declare (ignore url id))
(resolve-string "<node />" ctxt))

MY-RESOLVE-1
TREE> (defun my-resolve-2 (url id ctxt)
(declare (ignore id))
(if (eql (puri:uri-scheme url) :my)
(resolve-string (format nil "<node>~A</node>" url) ctxt)))

MY-RESOLVE-2
TREE> (with-custom-resolvers (#'my-resolve-2 #'my-resolve-1)
(with-parse-document (doc "<root xmlns:xi=\"http://www.w3.org/2001/XInclude\">
<xi:include href=\"my:doc\" />
<xi:include href=\"my2:doc\" />
</root>")
(process-xinclude doc)
(serialize doc :to-string)))

"<?xml version=\"1.0\" encoding=\"utf-8\"?>
<root xmlns:xi=\"http://www.w3.org/2001/XInclude\">
<node>my://doc</node>
<node/>
</root>
"

XPath extension functions

XPath мощный инструмент, но часто встроенной функциональности не хватает. В таком случае можно добавить пару своих функций:

TREE> (in-package #:libxml2.xpath)
#<PACKAGE "LIBXML2.XPATH">
XPATH> (define-xpath-function hello-world ()
"Hello world!")

HELLO-WORLD
XPATH> (define-xpath-function echo (msg)
msg)

ECHO
XPATH> (define-xpath-function join (delimiter &rest strs)
(iter (for str in strs)
(reducing str
by (lambda (s x) (concatenate 'string s delimiter x)))))

JOIN
XPATH> (with-xpath-functions ((hello-world "hello-world")
(echo "echo")
(join "join"))
(with-parse-document (doc "<root />")
(find-string doc "join('//', hello-world(), '---', echo('Buy!'))")))

"Hello world!//---//Buy!"

XSLT extension elements

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

XPATH> (in-package #:libxml2.xslt)
#<PACKAGE "LIBXML2.XSLT">
XSLT> (define-xslt-element copy-of (self input output)
(iter (for node in-xpath-result (attribute-value self "select") on input)
(append-child output (copy node))))

COPY-OF
XSLT> (with-xslt-elements ((copy-of "copy-of" "www.sample.org"))
(with-stylesheet (style "<?xml version=\"1.0\"?>
<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:my=\"www.sample.org\" extension-element-prefixes=\"my\" version=\"1.0\">
<xsl:template match=\"/root\">
<result><my:copy-of select=\"node()[@attr]\" /></result>
</xsl:template>
</xsl:stylesheet>")
(with-parse-document (doc "<root><a attr=\"1\"/><b /><c attr=\"2\"/><d /></root>")
(with-transfom-result (res (style doc))
(serialize res :to-string)))))

"<?xml version=\"1.0\" encoding=\"utf-8\"?>
<result>
<a attr="1"/>
<c attr="2"/>
</result>

Резюме

Кстати, "custom URL resolving" и "XPath extension functions" работают и внутри XSLT.
Когда-то меня впечатлили соответствующие возможности библиотеки lxml: оказалось что реализовать сопоставимый функционал не так уж и сложно :-)

среда, 12 ноября 2008 г.

Управление ресурсами в Common Lisp

Вот за что люблю C++, так это за деструкторы (ну, конечно, не только за это). Последовательное следование принципу RAII привело к тому, что в течении последних нескольких лет активного использования C++ я не имел ни одной проблемы, связанной с утечкой ресурсов, даже когда писал с использование ObjectARX (Autocad), а это жуткий монстр, настоящее минное поле: шаг в сторону (ну в документации то про это ни слова) и все - лезут проблемы, но в каком-нибудь совершенно другом месте... Поэтому все байки о том, что при программировании на C++ есть какие-то большие проблемы с управлением ресурсами никогда всерьез не принимал: так было когда-то давно (ну когда компьютеры были большими, а программы маленькими), сейчас же достаточно просто последовательно использовать современным концепциям (RAII, интеллектуальные указатели, безопасная обработка исключений) и все эти рассказы о трехсуточных отладках можно смело отнести к области программистского фольклора былых дней.

Ну да ладно, что-то ударился в воспоминания: уже больше года на C++ не пишу (и рад бы, да не знаю куда его можно применить, не над теми задачами сейчас работаю). В Lisp это всё якобы не имеет большого значения, ибо есть сборка мусора. Нет, ну может оно так и есть для Lisp-машин, но я с ними не работал. А реальность состоит в том, что в основе значительной части современной IT-инфраструктуры лежит язык "C". А значит, сокеты, каналы, соединения с базой и т.п. сборщик мусора никак обработать не может. Но даже если бы мог, то вряд ли бы это улучшило ситуацию, часто эти ресурсы необходимо освобождать в четко определенные моменты и не ждать когда же запуститься сборка мусора (вот не закроешь канал и останется висеть дочерний процесс в памяти, и последствия мало предсказуемы: вдруг он ждет завершения ввода для начала выполнения основной работы).

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

"Lisp way" для решения этих проблем заключается в использовании макросов with-... Часто такой способ и удобен и эффективен. Но не всегда. Возьмем такой надуманный (и довольно глупый) пример:
(defun myprint (text stream)
(write text :stream stream))
Что будет, если в качестве stream будет передан nil? Ну, текст будет выведен в *standard-output*. А если необходимо другое? Скажем, как нибудь так:
(defun myprint (text stream)
(if stream
(write text :stream stream)
(with-open-file (out (print (sb-posix:mktemp "/tmp/XXXXXX")) :direction :output)
(write text :stream out))))
Т.е. мы получили дублирование кода. Благо это пока только одна строка (write text :stream stream). А если там не одна строка? А много-много строк? Ну хорошо, можно сделать так:
(defun myprint (text stream)
(flet ((myprint-impl (text stream)
(write text :stream stream)))
(if stream
(myprint-impl text stream)
(with-open-file (out (sb-posix:mktemp "/tmp/XXXXXX") :direction :output)
(myprint-impl text out)))))
Вроде всё хорошо? Ну а если в функцию передается несколько потоков и каждый из них может иметь значение nil и необходимо для каждого такого параметра создавать поток самостоятельно. Вот тут начинаются проблемы, ибо with-open-file нам больше не поможет (разве что только через совершенно жуткий код, пример которого я даже приводить боюсь). Нет, ну по настоящему мужественные люди возьмут в руки unwind-protect, что фактически эквивалентно переходу на ручное управление ресурсами, и без особых проблем напишут необходимый код. Только я так делать боюсь. Ибо это требует аккуратного кодирования, а славное прошлое C++-программиста научило меня избегать подобных вещей.

Подобной ситуации с потоками у меня пока ещё не было (да и вряд ли будет, пример то надуман), но схожие ситуации при работе, например, с cffi встречают регулярно, и выглядит это примерно так:
(defun myfun (str1 str2 str3 str4)
(cffi:with-foreign-strings ((%str1 str1) (%str2 str2) (%str3 str3) (%str4 str4))
(c-фунция %str1 %str2 %str3 %str4)))
Всё отлично, вот только с-функция может принимать, как это часто бывает, в том числе NULL-ы, а значит в myfun можно передать несколько nil. Для передачи NULL в с-функцию необходимо использовать (cffi:null-pointer), но вот беда - cffi:with-foreign-strings ломается, если туда передавать nil. Приходиться брать в руки unwind-protect и заниматься ручным управлением памятью, т.е. почувствовать себя "многострадальным С-программистом", со всеми вытекающими.

Однако, "многострадальные С-программисты" не так просты и их страдания малость преувеличены :-) В частности, разработчики Apache, для которых "ресурсы" святы (к чему приведут утечки ресурсов за несколько месяцев работы?) уже давно придумали эффективный и удобный механизм, который можно для краткости назвать APR Pools. Не вдаваясь в подробности, суть в том, что при выделении ресурса его необходимо немедленно зарегистрировать в пуле (их есть несколько, для запроса, для соединения и т.п.) и система автоматически освободит его (ресурс) при разрушении этого пула. Всё, имеющийся API делает проблему управления ресурсами тривиальной.

Надо же, С-хакеры оказались в этом куда более продвинуты :-) Ситуация является неприемлемой. Эта мысль привела меня к созданию garbage-pools: реализации подобия APR Pools на Common Lisp. С помощь этого несложного инструмента предыдущий пример можно написать так:
(defun myfun (str1 str2 str3 str4)
(flet ((foreign-string (str)
(if str
(gp:cleanup-register (cffi:foreign-string-alloc str)
#'cffi:foreign-string-free)
(cffi:null-pointer))))
(gp:with-garbare-pool ()
(с-функция (foreign-string str1)
(foreign-string str2)
(foreign-string str3)
(foreign-string str4)))))
garbage-pools имеет совсем небольшой API:
  • pool - класс пула ресурсов
  • with-garbare-pool - макрос создающий и разрушающий пул. Возможно использование как именованных, так и безымянных пулов, например:
    (with-garbare-pool ()
    (cleanup-register myobj clenup-fun))
    (with-garbare-pool (mypool)
    (cleanup-register myobj clenup-fun mypool))
  • cleanup-register - регистрирует в пуле объект и "уничтожающую"-функцию
  • cleanup-pool - вызывает разрушение всех зарегистрированных объектов
  • cleanup-object - вызывает "преждевременное" разрушение зарегистрированного объекта
  • object-register - "generic"-метод для регистрации объектов в пуле без указания функции "разрушителя". Это имеет смысл, когда функцию-разрушитель можно определить по типу объекта (из коробки поддерживаются только stream и pool)
  • defcleanup - макрос, позволяющий сопоставить указанному классу функцию-разрушитель (после чего объекты данного классы можно будет регистрировать с помощью object-register). Пример:
    (defcleanup stream #'close)


Lisp Logo

Довольно случайно наткнулся на забавный Lisp Logo. Выглядит это так (на самом деле там несколько вариантов):



Такая картинка показалась мне гораздо интереснее, чем унылый



Посему, оное "страшное" предупреждение и было добавлено на главную страницу cl-libxml2 :-)

вторник, 28 октября 2008 г.

cl-libxml2-0.1

Спустя две недели после начало работы (в основном, ночной), свершилось - cl-libxml2 версии 0.1 доступна для использования, критики и прочего ... Это, конечно, только начало, но с этим уже вполне можно работать, а самое главное есть вполне вменяемая поддержка xpath и xslt.

И надо же было так совпасть, что релиз совпал с моим днем рождением :-) Типа сделал сам себе подарок :-)

понедельник, 13 октября 2008 г.

cl-libxml2

В предыдущем сообщении я рассказал об идеи сервера-приложений, которая меня уже давно привлекает и над зачатками которой я уже успел поработать. Но после окончательной кристаллизации идеи выявилась проблема - я не хочу писать это на Python. Не то, чтобы я имел что-то против Python, очень хороший, милый такой язык :-), но не восхищает. Я не получаю такого удовольствия от программирования на Python, как от программирования на Common Lisp. Меня давно подмывало реализовать эту идею на CL, но тут выходит на сцену относительная малочисленность lisp-разработчиков и некоторый недостаток библиотек.

В данном случае, речь идёт о недоступности полноценного решения для работы с XML. Да, cxml, вкупе с cxml-stp - отличная библиотека, но там не реализовано критически важная для меня поддержка XSLT. В принципе, близка к первому релизу Xuriella XSLT (основанная на cxml), но, как мне сообщил один из разработчиков, с производительностью у этой библиотеки пока беда... Это, вероятно, поправимо, но со временем, причем, не вполне определённым. Вообще, мне представляется довольно странным, что в открытом доступе нет обёрток для libxml2. Мне кажется, что есть некоторая нелюбовь lisp-хакеров к написанию оберток над библиотеками, написанными на других языках, возможно некоторым это кажется унизительным :-)

В общем, отчаявшись найти устраивающее меня решение для работы с xml я наконец решился начать делать самому cl-libxml2. В основу я положил CFFI, а в дизайне ориентируюсь на cxml-stp, за тем исключением (весьма существенным), что для обработки списков (элементов, атрибутов и т.д.) будут использоваться решения на базе iterate.

воскресенье, 31 августа 2008 г.

Забавно, сегодня предложили разместить в своем блоге вот это:
Клавогонки.Ру
Размещаю, хотя результат ещё слабоват :-)

вторник, 26 августа 2008 г.

Глобальные переменные в Common Lisp и let

С самого начал изучения программирования в умных книжках читал об "ужасах" использования глобальных переменных. Поскольку я склонен доверять "умным людям", то всегда избегал использования таких средств. Правда, был период, когда находясь под сильным влиянием Александреску широко использовал Singleton-ы - чудом избежал больших проблем, утвердился на собственном опыте, что глобальные переменные - зло, а пресловутый паттерн "одиночка" совершенно не решает свойственных им проблем.

Но вот я стал изучать Common Lisp и роясь в исходников многих библиотек обнаружил, что lisp-разработчики совершенно не стесняются использовать глобальные переменные. Что это? Код был написан профессионально и качественно, но широкое использование глобальных переменных меня просто шокировало. Разве они (разработчики) не знают что это очень опасно? К тому же, конкретно библиотека cl-pdf вызывала у меня серьезные опасения за корректностью своей работы в условиях многопоточности (использовалась в рамках веб-сервера hunchentoot). Поскольку система, всё же, работала нормально, то видимо была какая-то тонкость, которую я не понимал. Посему, просто отложил этот факт в памяти и вернулся к работе.

Первым шагом к пониманию стало прочтение в SICP описания "модели вычислений с окружением". Знания получил, но понимания тогда ещё не обрёл :-)

Наступлению прозрения, как это часто бывает, способствовало несчастье :-) При работе с cl-pdf в sbcl при текущей локали ru_RU.UTF-8 у меня неверно обрабатывался русский текст и получались всякие невменяемые закорючки. Исследование выявило проблему не в самой cl-pdf, а в chunga (при работе под sbcl). Проблему можно было решить либо небольшим патчем (буквально, переделать одну строку), что мне не очень нравилось, либо в моём коде за счёт временного изменения переменной sb-impl::*default-external-format*, которую следовало временно установить, например, в :latin-1. Первоначально я написал примерно следующее:
(let ((old-external-format  sb-impl::*default-external-format*))
(setf sb-impl::*default-external-format* :latin-1)
(|Здесь код по выводу сгенерённого pdf-документа в поток|)
(setf sb-impl::*default-external-format* old-external-format))
Необходимого эффекта я добился, но код, конечно, совершенно ужасен. Во-первых, не обрабатываются возможные исключения, в результате чего, измененная глобальная переменная может так и не восстановить свое прежнее значение (конечно, можно добавить обработку исключений, но смотреть будет еще страшнее). Во-вторых, система изменяются глобально, что в условиях многопоточности чревато большими проблемами (наступление которых - лишь вопрос времени). Короче, весьма характерный пример проблем с глобальными переменными. И тут я понял:
(let ((sb-impl::*default-external-format* :latin-1))
(|Здесь код по выводу сгенерённого pdf-документа в поток|))
Это работает! Здесь значение глобальной переменной sb-impl::*default-external-format* связывается с новым значение, которое действует только внутри блока let и совершенно не затрагивает остальную систему. И это потрясающе! Такая возможность решает изрядную долю проблем, связанных с использованием глобальных переменных и позволяет делать код проще без потери гибкости за счёт возможности передавать аргументы не через формальные аргументы функций, а через "окружение".

P.S. lispnik, после прочтения данного соообщения, указал ссылку на свой старый пост, в котором данная тема раскрыта горараздо лучше, при чём на основе теории и спецификаций (а не практики и эксперимента, как в моём случае). Прочитать можно здесь: http://lispnik.livejournal.com/9137.html

Первая запись

Таки решился завести себе блог. В общем, заметил что-то стал хуже писать различные документы и подумал, что нехватает практики. В общем, цель заведения блога - писать больше и лучше. Посмотрим что из этого получится.