Мне часто доводилось слышать, что в Common Lisp библиотеки из 80ых, и других вобще нет, а множество его пользователей представляет собой 3.5 профессоров, работающих над искусственным интеллектом в катакомбах DARPA и не имеющих даже близкого представления о повседневных задач рядового программиста. Данная статья хорошо показывает, что хоть вы и можете наткнуться на старые библиотеки, но использовать стоит лишь современные аналоги, что существующие библиотеки развиваются и постоянно появляются новые.
Автор этой статьи, Fernando Borretti, является активным контрибьютором в экосистему Common Lisp'а, автор более 30 библиотек, большинство из которых предназначены для веб-разработки.
Проходящий мимо читатель получит общее представление о состоянии дел в Common Lisp, интересующийся сможет понять что ему необходимо чтобы попробовать написать код и какие библиотеки взять для пробной задачки, а опытный разработчик узнает о самых последних разработках, поймет, библиотеками в каких направлениях он сможет помочь сообществу и получит несколько советов о том, как отвечать на вопросы новичков, чтобы не убить в них интерес к этой замечательной технологии. Поехали!
Здесь представлено описание экосистемы Common Lisp на август 2015 с точки зрения пользователя и контрибьютора.
Цель этой статьи — дать обзор экосистемы и помочь введению унификации в каждой области.
Для каждой прикладной области указаны рекомендации по унификации этой части экосистемы, а так же интересные направления будущего развития.
Разработка приложений
Веб разработка
Backend
Clack, аналог WSGI/Rack существует с 2009 года и хорошо протестирован и работает в продакшене. Три веб фреймворка — Caveman2, Ningle и Lucerne созданы на его основе.
Clack это абстракция HTTP сервера, которая позволяет пользователю писать веб фреймворки (или лучше фреймворки веб приложений) без завязки на конкретную реализацию сервера.
Важность использования Clack нельзя недооценивать: если вы делаете приложение непосредственно, на скажем, Hunchentoot, вы привязаны к Hunchentoot, и если появляется новый, более быстрый сервер, типа Woo, вам придется переписывать все приложение чтобы использовать его. Если вы пишете плагин для Clack, например clack-errors, он автоматически может быть использован всеми приложениями, независимо от фреймворков, сделанных на Clack, уменьшая бесполезное дублирование кода.
С Clack переключение с Hunchentoot на Woo и радость от потрясающего ускорения это всего лишь вопрос установки libev и изменения одного ключевого аргумента.
Заключение:
Прекращайте использовать Hunchentoot напрямую. Используйте Clack, или еще лучше, один из фреймворков на его основе.
Будущая работа:
Основная часть закончена, настало время писать высокоуровневые вещи. Расширяемый фреймворк администрирования Clack приложений, как в Django будет хорошим примером.
Frontend
Тут все связано с возможностью Common Lisp'а компилироваться в JavaScript. Common Lisp довольно большой язык, а вариантов немного:
- Parenscript: DSL который компилирует подмножество Common Lisp в идиоматичный JavaScript
- JSCL: саморазвертывающийся компилятор из CL в JS. Отсутствует поддержка CLOS, format и loop. (в отличие от Parenscript представляет собой интерпретатор в браузере — прим. переводчика)
Заключение:
Лучший путь к унификации это развитие одной из существующих реализаций CL-в-JS.
Будущая работа:
Что-то наподобие CFFI, но для реализации CL-в-JS, чтобы не приходилось переписывать биндинги к JavaScript библиотекам при выходе новых версий библиотек.
Утилита для компиляции Common Lisp в JavaScript независимая от конкретной реализации, чтобы облегчить переход от Parenscript к JSCL или другим реализациям.
Командная строка
Многие годы различные утилиты появлялись в этой области, последняя из них, та, что получила самый большой импульс — Roswell — реализация менеджера/установщика и запускателя скриптов. Одна ее из замечательных возможностей — легкая компиляция небольших скриптов в исполняемые файлы, например, для генерации документации.
Она так же используется для установки реализаций (CL) в Travis для обхода некоторых проблем с cl-travis.
Заключение:
Убейте cl-launch, используйте Roswell.
Будущая работа:
Больше Roswell скриптов.
Графический интерфейс
Длительное время большое беспокойство вызывало отсутствие полноценного решения для GUI. Теперь у нас оно есть: CommonQt плюс Qtools. Первое годами используется в реальных приложениях, а второе — прослойка, позволяющая делать все проще.
Самая большая проблема в использовании CommonQt — это требование работы Smoke, а заставить библиотеки работать может быть сложно, особенно в система отличных от Linux. Это решается благодаря Qtools, который зависит от библиотеки qt-libs. Он скачивает библиотеки Smoke для текущей платформы и делает настройку и деплой приложений намного проще.
Заключение:
Сфокусироваться на CommonQt и помочь в улучшении cl-cffi-gtk, а другие библиотеки считать устаревшими.
CLIM интересен и был последней попыткой исследования способов создания пользовательских интерфейсов, но он не является жизнеспособным вариантом в 2015 году.
Будущая работа:
Больше туториалов и примеров использования CommonQt и Qtools.
Машинное обучение
CLML довольно многофункциональное решение. Оно было разработано Mathematical Systems Inc., японской компанией. Mike Maul впоследствии перенес его на github и немного почистил код. Доступен туториал о временных рядах.
Другой кандидат в этой области это mgl, использовавшийся его автором для победы на Higgs Boson Machine Learning Challenge.
В области численных вычислений мне всегда была интересна библиотека Antik, но к сожалению она зависит от GNU Scientific Library, что заставляет ее распространяться под GPL. Так же есть mgl-mat и LLA.
Заключение:
Объединение частей для вычислений из LLA и mgl-mat и частей машинного обучения из CLML и mgl позволит решить большинство проблем в этой области.
Будущая работа:
CLML вероятно содержит много кода для работы с вычислениями который может вынесен в отдельную библиотеку по типу SciPy и NumPy.
Базы данных
cl-dbi предоставляет универсальный интерфейс к различным БД-специфичным библиотекам (cl-postgres, cl-mysql и тд). SxQL дает DSL для построения безопасных, автоматически параметризуемых SQL-запросов.
Тут есть два равно полноценных ORM: Crane от автора данного обзора, и Integral от автора cl-dbi.
Заключение:
Препятствовать использованию чего-либо кроме cl-dbi.
Будущая работа:
Биндинги для других баз данных, например Oracle, существуют. Написание драйверов для cl-dbi будет лучшим способом содействия и помощи унификации.
Графика
Я никогда не программировал графику, поэтому у меня мало знаний в этой области. Существует CEPL и родственный проект Varjo, с отличной коллекцей видео туториалов. Конечно, есть и такие низкоуровневые библиотеки как cl-opengl и cl-sdl2.
Заключение:
Продвигать использование CEPL, потому что он практически полноценен.
Будущая работа:
Высокоуровневая OpenGL библиотека, как pg, была бы замечательна.
Больше библиотек в этой области, особенно для манипулирования различными мешами и другими 3D форматами.
Конкурентность
cl-async вероятно наиболее полное решение для всего что связано с конкурентностью. И оно построено поверх libuv, библиотеки, стоящей за Node.js.
Другие примечательные библиотеки в этой области:
- STMX: предоставляет поддержку программной транзакционной памяти, и очень впечатляет.
- lparallel: полноценный фреймворк для параллельного программирования.
Библиотеки типа legion упрощают конкурентность для определенных случаев.
Заключение:
Будущая работа:
В этой области достаточно много места для новых идей.
Форматы файлов
В Common Lisp есть библиотеки для всех популярных форматов файлов:
Новичок среди JSON библиотек — Jonathan, очень быстрый JSON генератор и парсер.
Заключение:
Существует слишком много XML и JSON библиотек, это ведет к параличу выбора.
Будущая работа:
YAML парсер чтобы cl-yaml не зависел от libyaml, это сделает распространение намного легче.
Взаимодействие с системой
UIOP и уровень совместимости из ASDF содержат огромный набор утилит для портирования любой функциональности от запроса имени хоста до запуска внешних программ и манипуляций переменными окружения.
Заключение:
Используйте UIOP для всего. Не используйте cl-fad. Не используйте OSICAT.
Будущая работа:
Интересным проектом в этой области был бы DSL для генерации LLVM IR, либо как строку, либо используя биндинги LLVM API. Это бы позволило использовать Common Lisp как ассемблерный язык с невероятно мощной системой макросов. Например, чтобы JIT-компилировать сильно оптимизированный SIMD-векторизованный численный код.
Разное
Бенчмарки
Создание фреймворка наподобие Criterion в Haskell было бы большим шагом вперед.
Парсинг
Тут я обычно выбираю esrap, который генерирует парсеры из Lisp-подобных описаний грамматик. Нужно больше разнообразия в этой области. Недавно был релиз proc-parse, быстрой библиотеки векторного парсинга.
Логирование
Не много можно сказать об этом, кроме того, что Common Lisp имеет хорошие утилиты для логирования, такие как log4cl и vom.
Разработка
Система типов
Опять же нечего тут говорить, кроме того, что у Common Lisp довольно хорошая система типов, которая используется далеко не на полную катушку.
Заключение:
У CLOS нет конкурентов, среди выживших версий Flavors, и это практически единственная реализация ООП в Common Lisp.
Будущая работа:
Я часто смотрю на trivial-types, библиотеку, содержащую множество простых спецификаторов типов. Библиотека побольше, включающая этот CDR, была бы хороша.
Тестирование
Есть много фреймворков для тестирования, главные из них FiveAM и более новый Prove.
Из существующих библиотек, Prove хороший кандидат. Расширяемые генераторы отчетов отличная штука, и общих подход к расширяемости делает его хорошим решением проблемы тестовых фреймворков.
Другие, более старые библиотеки зависят от таких фреймворков как rt или lisp-unit, но они больше не дееспособны.
Заключение:
Препятствовать использованию тестовых фреймворков кроме FiveAM и Prove.
Будущая работа:
Написание новых тестовых фреймворков будет контр продуктивно в текущей ситуации. Работа должна быть сосредоточена на унификации существующих.
Тестирование в облаке
Common Lisp хорошо поддерживает использование сервисов типа Travis и Coveralls для тестирования кода и отслеживания покрытия в облаке соответственно. Две главных библиотеки для этого cl-travis и cl-coveralls и их обе очень легко использовать. По моему опыту тестирование и отслеживание покрытия проекта на Common Lisp определенно проще, чем тоже самое в проекте на Python.
Заключение:
cl-travis имеет несколько проблем, например, ему требуется sudo для установки реализаций, из-за этого он не может использовать новую основанную на Docker инфраструктуру в Travis.
Roswell решает эту проблему, поэтому рекомендуется использовать Roswell с cl-travis. Тут можно найти туториал по использованию Roswell с Travis.
Будущая работа:
Поддержка многих сервисов, таких как Circle CI, всегда полезно. Туториалы так же будут хорошим дополнением.
Документация
Для онлайновой, автоматически обновляемой документации в стиле Read the Docs есть Quickdocs, который достает описания API проекта с помощью docparser.
Существует неожиданно мало генераторов документации. Я использую Codex для генерации документации. Он написан на CommonDoc — библиотеке, предоставляющей формато-независимое внутреннее представление документов. Codex задуман похожим на Sphinx: документация пишется прозой, и вы вставляете автоматически извлекаемую документацию API (функции, макросы, классы и тд) в текст с помощью макросов.
Заключение:
Продвигать использование Quickdocs ссылаясь на него из README, советуя новым пользователям и т.д.
Будущая работа:
Я продолжу работу над Codex и CommonDoc и любые улучшения в CommonDoc (в основном новые выходные форматы и макросы) будут отражены в Codex.
Юникод
Это в сущности решенная проблема в большинстве реализаций. Иногда вы можете наткнуться на проблему, если установлена какая-то отличная от UTF-8 кодировка, но это легко исправляется, например в SBCL:
(setf sb-impl::*default-external-format* :utf-8)
Я никогда не встречал других проблем с юникодом, даже когда взаимодействовал с библиотеками на Си.
Заключение:
Некоторые реализации отстают в поддержке юникода, например ABCL.
Будущая работа:
Устранять проблемы в поддержке юникода можно улучшая библиотеку cl-unicode.
Управление пакетами
Quicklisp де-факто пакетный менеджер для Common Lisp. И этого вполне достаточно. Не разрушай то, что работает.
Заключение:
Все хорошо.
Будущая работа:
Утилиты на основе Quicklisp, типа Quickdocs и qlot.
Система сборки
ASDF де-факто система сборки для Common Lisp. Все используют ее.
Каждый проект имеет файл .asd, он называется определением системы, указывает метаданные проекта (автора, мейнтейнера, домашнюю страницу и тд) и составляющие.
Для меня, это одна из главных привлекательных особенностей Common Lisp. В языках типа Python каждый файл импортирует себе то, что ему нужно, и проект становится массивным графом перекрестных зависимостей файлов. В ASDF вы просто перечисляете файлы вашего проекта в порядке в котором они определены. Или вы можете указать зависимости между файлами и позволить ASDF вычислить порядок самому. Суть в том, что зависимости заданы в явно и отчетливо видны.
Но достаточно агитации. Главное, что у ASDF нет конкурентов не потому, что никто не хочет создать аналог, но потому что ASDF уничтожил все альтернативы несколько лет назад. И это хорошо.
Заключение:
Все хорошо.
Будущая работа:
Больше компонентов к ASDF, например для сборки файлов C/C++. Платформо-независимый пакетный менеджер для загрузки внешних Си-библиотек требуемых библиотеками Lisp был бы невероятно полезен.
Среды разработки
SLIME это среда разработки для Common Lisp основанная на Emacs и самая широко-распространенная. Тот факт, что она сделана на Emacs представляет проблему, ведь говорить новым пользователям что они должны изучить Emacs чтобы использовать SLIME (что не совсем правда) является значительной искусственной преградой к изучению CL.
Заключение:
Уменьшить входной барьер для использования SLIME.
Будущая работа:
Среды отличные от Emacs не могут навредить. Плагин для Atom, который работал бы со Swank-сервером был бы очень полезен.
Заключение
Специфичные проблемы
Общение
Число людей создающих веб приложения на Common Lisp и не знающих про Clack слишком велико. Очевидно, что есть проблема в информировании потенциальных пользователей о хороших библиотеках.
Паралич выбора
Когда кто-то спрашивает какую библиотеку использовать для написания кода в определенной области следует рекомендовать только лучшую библиотеку из данной области. Говорить «вы так же можете взять X, Y и Z» только ухудшает парадокс выбора.
Если кто-либо спрашивает библиотеку для графического интерфейса, отвечайте "CommonQt с Qtools", не добавляйте «но так же можешь взять cl-cffi-gtk или LTK...»
Если спрашивают что использовать для веб-приложений, делитесь ссылкой на один из фреймворков на Clack. Не говорите про Hunchentoot или Wookie или Woo или какой-то другой сервер.
Если кто-то спрашивает какую среду разработки использовать — указывайте им на SLIME. Не рассказывайте про какой-то мутный заброшенный проект из 2005 года который работает только с CLISP.
И самое главное: когда вас спрашивают какую реализацию использовать — просто отвечайте SBCL или CCL. Не говорите «ну, ты так же можешь взять ECL для встраивания или ABCL чтобы работать на JVM». Они для использования в более узких случаях, не для того чтобы дать быстрый старт новичку.
Развитие
Загрузки с Quicklisp
Ниже представлено общее количество скачиваний для ста самых популярных проектов из Quicklisp, между январем и июлем:
Репозитории
Я подписан на эту ленту новый репозиториев на Common Lisp на Github. Я так же написал немного кода для запросов в базу Newsbeuter:
(ql:quickload (list :dbi :yason :local-time :group-by))
(defvar *connection*
(dbi:connect :sqlite3
:database-name (merge-pathnames #p".newsbeuter/cache.db"
(user-homedir-pathname))))
(let* ((output (merge-pathnames #p"lisp-github.json"
(user-homedir-pathname)))
(query (dbi:prepare *connection*
"SELECT pubDate FROM rss_item WHERE feedurl = ?"))
(results (mapcar #'(lambda (result)
(local-time:unix-to-timestamp
(getf result :|pubDate|)))
(dbi:fetch-all
(dbi:execute query "http://planet.lisp.org/github.atom")))))
(with-open-file (stream output
:direction :output
:if-exists :supersede
:if-does-not-exist :create)
(yason:encode
(mapcar #'(lambda (group)
(list
(local-time:format-timestring nil
(first group)
:format (list :year #\- :month #\- :day))
(1- (length group))))
(group-by:group-by-repeated results
:keys (list #'identity)
:tests (list #'(lambda (a b)
(= (local-time:day-of a)
(local-time:day-of b))))))
stream)))
С 23 марта 2015 по 20 августа 2015 было создано 882 репозитория.
Благодарности
Спасибо Javier Olaechea за его комментарии о поддержке юникода, Eitaro Fukamachi и Francois-Rene Rideau, и Gabriel Gonzalez за оригинальную статью о состоянии экосистемы Haskell.
Комментарии (10)
Ununtrium
28.08.2015 16:28+2А вот скажите, чем Common Lisp лучше того-же Clojure? Не холивар, серьезно интересно.
loz
28.08.2015 17:06Они очень разные, Common Lisp это самостоятельный, полноценный, мультипарадигменный язык с составлявшимся годами стандартом. Clojure сложно назвать самостоятельным. Насколько мне известно, например в нем нет собственной реализации объектной системы, используется доступная в JVM, в нем нельзя управлять стеком вызовов и сделать аналог condition-restart из Common Lisp, даже хвостовая рекурсия поддерживается только через специальный оператор recur. Вобщем все ограничения (и плюсы) JVM так или иначе отражаются на нем.
Common Lisp в противовес определяется стандартом, а не идеями одного человека и ограничениями платформы, так он не позиционируется как функциональный с точки зрения иммутабельности данных (это есть в библиотеках), дает контроль над ридером благодаря ридер-макросам (в кложуре появилось что-то отдаленно похожее для определения текущей платформы, clojure или clojurescript) и тд.
По-моему Clojure это больше скриптовый язык для Java-приложений, а Common Lisp для написания на нем всего что может быть нужно, вплоть до ассемблерного кода.Ununtrium
31.08.2015 13:50Спасибо за ответ. Со стороны Clojure люди говорят, что другие лиспы для современных (читай, многопоточных) приложений подходят слабо, было интересно мнение другой стороны.
loz
31.08.2015 14:23На самом деле интересно, сколько из этих людей имели опыт с CL. Просто в кложу встроены примитивы для многопоточности, а CL они находятся в отдельных библиотеках (типа STMX, lparallel). По-моему, решения встроенные в язык кажутся людям лучше и надежнее.
igrishaev
28.08.2015 17:12Например, в кложуре стандартные джавные исключения из-за ограничений платформы. И крайне неинформативные трейсы на 100 уровней.
karlkorp
28.08.2015 20:26+2БОЛЬШОЕ СПАСИБО за Вашу статью!!! Пошел смотреть на CommonQT… Меня очень радует тот факт, что такой классный язык как Common Lisp развивается, появляются новые библиотеки и фреймворки. Это — КРУТО!!!
kuznetsovin
28.08.2015 21:00А как насчет Racket? Может быть кто-нибудь использова и то и другое? Я сейчас как раз вибираю между ними.
loz
28.08.2015 21:40+1Я пробовал года 3 назад, написал модуль с кастомной авторизацией толи для апача, толи нгинкса и веб интерфейс к сканеру через sane. Собственно с обоими задачами хорошо справился, у Racket из коробки огромная стандартная библиотека (в CL все это тоже есть, только отдельным пакетами), и подробная документация, так что начать с ним проще. Но количество библиотек в CL значительно больше, и, так скажем они более production ready. Хотя это субъективно и может на текущий момент что-то да изменилось.
Порекомендую CL как более практичный вариант.
Rigidus
Большое спасибо за перевод.
Я один из тех, кто разрабатывает веб-приложения на hunchentoot вместо использования лучшего. Теперь присмотрюсь к Clack. Собственно ушел смотреть :)
loz
Меня очень заинтересовал этот JSCL, 170Kb в скомпиленном виде, и список символов на пакет CL выдает 900+, может заглушки, но все равно неплохо. Надо понять какой из него есть интерфейс к JS и можно сделать аналог OM, о котором мы говорили на конфе =)