Путешествие по миру Clojure библиотек проходит не так гладко, как планировалось. Отцы-основатели подали хороший пример в clojure.core и сlojure.contrib (ныне org.clojure/*), но соответствовать не у всех получается.
Сначала Storm (который Twitter Storm) опечалил своим ООП-подходом, когда вместо отдельно данных и функций всё сваливается в кучу. Это, кажется, идет от его Java-корней, Clojure там приделали позже. Держаться можно, если спускаться в Storm только в самый последний момент.
Вторым моим неудачным опытом стал Midje, который рекламирует себя так:
Midje is a test framework for Clojure. I created it to support top-down as well as bottom-up testing, to encourage readable tests, to provide a smooth migration path from clojure.test, to support a balance between abstraction and concreteness, and to be gracious in its treatment of you, my valued guest.
На словах у них все действительно прикольно; лезешь в дебри, и вроде бы тоже круто - куча всякого мяса на все случаи жизни, и такой ассершн, и сякой, и пятый, и десятый; кажется, что больше ничего никогда не понадобится (в отличие от clojure.test, в котором есть только is и are и больше ничего и вообще непонятно, как же им тогда пользоваться). На радостях я начал его тянуть к себе и сразу понадобилось. Тут стало понятно, что Midje на самом деле весь про красивую запись, чтобы писать не
(is (= (f x) 2))
а
(fact (f x) => 2)
или, допустим, не
(are [k v] (= (m k) v)
:a 1
:b 2)
а
(fact m => (contains {:a 1 :b 2})
И хотя философия Clojure не запрещает красоты и удобства (зачем иначе макросы?), помимо них во главу угла ставятся также простота как отсутствие составных частей и стыкуемость абстракций. Midje же, если к нему присмотреться, родился из желания автора писать поменьше символов. Заявленное, конечно, работает, но эта стрелочка и разные удобные штуки правой части выражения - это очень страшное макропрограммирование, которое делает кучу сложных предположений об устройстве тестовых форм.
Получилась вещь в себе - если постараться, можно написать свою contains-in функцию с кучей особенностей для Midje и она будет работать в правой части (хотя добиться, чтобы она печатала что нужно в случае ошибки мне не удалось), а можно взять просто функцию get-in и дубовый clojure.test и написать (is (= (get-in ...))) и будет логично, удобно и просто. Если проверок несколько, это заворачивается в прекрасный (и гениально универсальный) макрос are и это единый способ на все случаи жизни. Оказывается, все что нужно у нас давно уже есть и его можно и нужно использовать в тестах. Воистину, лучше научить человека ловить рыбу... Или, скажем, надо поменять формат output - в сlojure.test сразу понятно как это сделать, а в Midje даже авторы затрудняются - потому что он внутри правда очень сложный. Зато, типа, символов мало писать.
Или посмотрел я вчера на что у нас есть для Redis. Гуглится
такое:
- A high-performance, all-Clojure client.
- Modern targets: Redis 2.0+ (with full 2.6 support), Clojure 1.3+, Leiningen 2 support (not mandatory).
- Industrial strength connection pooling.
- Complete and accurate command definitions with full documentation.
- Composable, first-class command functions.
- Flexible, high-performance binary-safe serialization.
- Full support for Lua scripting, Pub/Sub, etc.
- Full support for custom reply parsing.
- Command helpers (atomically, lua-script, sort*, etc.).
- Ring session-store.
- Simple, high-performance message queue (Redis 2.6+).
По первом прочтении я чуть от радости не запрыгал - надо же, какие классные ребята, как правильно пишут! Потом замечаешь эпитеты в каждом предложении, как будто дезодорантом пытаются спрятать что-то не слишком приятное само по себе (я уверен, что это маркетинг Эппл диктует моду - на сайте любого мак-приложения будет написано «The simplest way to ...», а написать вместо этого по делу яиц не хватает). Потом, конечно, читаешь, что «Simple, high-performance message queue (Redis 2.6+)» у них означает «Currently ALPHA QUALITY». Потом глаз цепляется за Leiningen 2 (зачем redis-библиотеке интеграция с системой сборки?), а потом видишь, что они еще и логгер свой туда притащили (зачем redis-библиотеке логгер? Я не знаю. Самописный, с самобытным конфигом - как его в приложение-то интегрировать?). Сериализация тоже самописная, по умолчанию она все объекты, даже примитивы, заворачивает так, что Redis нихрена не понимает, что у него там хранится (зато, типа, для разработчика «This scheme is consistent, unambiguous, and simple»).
Такой вот хипстерский лагерь - и вера в то, что на одном вкусе можно выехать, и что вкус в программировании (и простота тоже) - это написать поменьше символов, и поливание абстрактными, ничего не значащими комплиментами самого себя. Рич как-то призывал общественность расфокусироваться с удобства программиста и сфокусироваться на реальных, измеримых качествах получаемого продукта. Я ему верю, потому что каким-то образом все, к чему притрагивались ребята из clojure.core - оно работает, оно работает просто, оно меняется, расширяется, соединяется и переиспользуется в нужных местах. Так вот, кто-то его слушал, а кто-то в этот момент хвастался во дворе перед друзьями новой красивой, но бессмысленной вещью. Но красивой. В общем, как бы ни хотелось, мне с этими ребятами, кажется, не по пути. Лично я это самолюбование и взаимное обмазывание хипстерскими ценностями не смогу сколь-нибудь долго читать, для душевного спокойствия гораздо лучше
что-то такое.
Кстати, пока писал, подоспела статья на ту же тему:
clojurefun.wordpress.com/2012/08/17/composition-over-convention. Всем чмоки. Бороду уже начал растить (или бороды тоже хипстеры заняли?).