<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4007342736133040943</id><updated>2012-06-04T17:34:35.082+03:00</updated><category term='leiningen'/><category term='computer science'/><category term='макросы'/><category term='noir'/><category term='emacs'/><category term='use cases'/><category term='compojure'/><category term='java'/><category term='clojure'/><category term='web'/><category term='DSL'/><category term='storm'/><category term='enlive'/><category term='алгоритмы'/><category term='lisp'/><category term='hiccup'/><category term='clojurescript'/><category term='общение'/><title type='text'>Clojure</title><subtitle type='html'>Все о clojure и функциональном программировании</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default?start-index=26&amp;max-results=25'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>51</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-7910071235775186798</id><published>2012-05-26T11:59:00.000+03:00</published><updated>2012-05-26T12:03:59.838+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='clojurescript'/><title type='text'>Впечатления о ClojureScript</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;На прошлой неделе решил наконец ознакомиться с ClojureScript по-подробнее. До этого я смотрел презентацию Рича Хикки и некоторые другие скринкасты, успел "пощупать" ClojureScriptOne и почитать пару статей о том, как он (ClojureScript) крут. В общем, никаких особых сложностей с ним я не ожидал, поэтому начавшиеся приключения стали для меня откровенным сюрпризом.&lt;br /&gt;&lt;br /&gt;Во-первых, мне не удалось создать browser-connected repl, соединенный со своей html-иной. Я выполнял простые туториалы шаг за шагом, и на этапе соединения у меня ничего не получалось. Тогда я запустил проект samples/repl, входящий в поставку ClojureScript. И все было хорошо до тех пор, пока я не попытался соединиться с веб-страницей. В результате, при помощи Дэвида Нолена и еще нескольких разработчиков ClojureScript выяснилось, что документация чуть-чуть не соответствует. После моих гневных отзывов её (документацию) наконец поправили, а я напишу здесь русский вариант туториала о том, как начать работать с ClojureScript.&lt;br /&gt;&lt;br /&gt;1. Качаем с &lt;a href="https://github.com/clojure/clojurescript"&gt;гитхаба&lt;/a&gt; последнюю версию ClojureScript.&lt;br /&gt;2. Настраиваем emacs:&lt;br /&gt;&amp;nbsp; a) Создаем файл browser-repl.sh с правами на выполнение:&lt;br /&gt;#!/bin/sh&lt;br /&gt;&lt;br /&gt;if [ "$CLOJURESCRIPT_HOME" = "" ]; then&lt;br /&gt;&amp;nbsp; CLOJURESCRIPT_HOME="`dirname $0`/.."&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;CLJSC_CP=''&lt;br /&gt;for next in lib/: lib/*: src/clj: src/cljs: test/cljs; do&lt;br /&gt;&amp;nbsp; CLJSC_CP=$CLJSC_CP$CLOJURESCRIPT_HOME'/'$next&lt;br /&gt;done&lt;br /&gt;&lt;br /&gt;java -server -cp $CLJSC_CP clojure.main -e \&lt;br /&gt;"(require '[cljs.repl :as repl])&lt;br /&gt;(require '[cljs.repl.browser :as browser])&lt;br /&gt;(def env (browser/repl-env))&lt;br /&gt;(repl/repl env)"&lt;br /&gt;&lt;br /&gt;б) Прописываем в .emacs настройки для inferior-lisp'a:&lt;br /&gt;(setq inferior-lisp-program "~/clojure/clojurescript/browser-repl.sh")&lt;br /&gt;&lt;br /&gt;Здесь "~/clojure/clojurescript/browser-repl.sh" -- естственно, путь к созданному на шаге 2а файлу browser-repl.sh.&lt;br /&gt;&lt;br /&gt;3. Добавляем clojurescript/bin в $PATH. &lt;br /&gt;&lt;br /&gt;4. Заходим в каталог clojurescript/samples/repl, компилируем исходники: cljsc src/ &amp;gt; main.js&lt;br /&gt;&lt;br /&gt;Файл main.js будет позднее использоваться в index.html&lt;br /&gt;&lt;br /&gt;Обратите внимание на содержимое файла test.cljs. Он содержит такую строчку:&lt;br /&gt;(repl/connect "http://localhost:9000/repl")&lt;br /&gt;Именно она позволяет затем соединиться с Emacs-ом.&lt;br /&gt;&lt;br /&gt;5. Файлы index.html, main.js и каталог out необходимо захостить на каком-нибудь веб-сервере. Я, например, просто создал каталог Tomcat7/webapps/my и сложил туда все это барахло. В моем случае страница index.html стала доступна по адресу localhost:8080/my/index.html.&lt;br /&gt;&lt;br /&gt;Внимание! Хостить страницу обяхательно, просто открыв её как файл с диска вы&amp;nbsp; не сможете к ней подконнектиться из Emacs-а!&lt;br /&gt;&lt;br /&gt;6. Запускаем Emacs, выполняем команду M-x inferior-lisp.&lt;br /&gt;&lt;br /&gt;7. Вводим следующие команды:&lt;br /&gt;(require '[cljs.repl :as repl])&lt;br /&gt;(require '[cljs.repl.browser :as browser])&lt;br /&gt;(def env (browser/repl-env))&lt;br /&gt;(repl/repl env)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;8. Загружаем/обновляем index.html в броузере. После этого repl в Emacs-е должен заработать. Например, вычисление (+ 1 1) должно выдать ответ 2. Если это произошло -- вуаля! у вас веб-консоль прямо в Emacs-е!&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-qQT9BLeDkDM/T8CYs2ii6HI/AAAAAAAAAM4/PAm5KuBpA7I/s1600/clojurescript.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="473" src="http://3.bp.blogspot.com/-qQT9BLeDkDM/T8CYs2ii6HI/AAAAAAAAAM4/PAm5KuBpA7I/s640/clojurescript.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;В моем примере index.html содержит следующий фрагмент:&lt;br /&gt;&lt;br /&gt;&lt;pre id="line1"&gt;      &amp;lt;&lt;span class="start-tag"&gt;div&lt;/span&gt; &lt;span class="attribute-name"&gt;id&lt;/span&gt;="&lt;a class="attribute-value" href="http://www.blogger.com/blogger.g?blogID=4007342736133040943"&gt;dates&lt;/a&gt;"&amp;gt;&lt;br /&gt; &amp;lt;&lt;span class="start-tag"&gt;div&lt;/span&gt;&amp;gt;23.05.2012&amp;lt;/&lt;span class="end-tag"&gt;div&lt;/span&gt;&amp;gt;&lt;br /&gt; &amp;lt;&lt;span class="start-tag"&gt;div&lt;/span&gt;&amp;gt;25.05.2012&amp;lt;/&lt;span class="end-tag"&gt;div&lt;/span&gt;&amp;gt;&lt;br /&gt; &amp;lt;&lt;span class="start-tag"&gt;div&lt;/span&gt;&amp;gt;27.05.2012&amp;lt;/&lt;span class="end-tag"&gt;div&lt;/span&gt;&amp;gt;&lt;br /&gt; &amp;lt;&lt;span class="start-tag"&gt;div&lt;/span&gt;&amp;gt;28.05.2012&amp;lt;/&lt;span class="end-tag"&gt;div&lt;/span&gt;&amp;gt;&lt;br /&gt; &amp;lt;&lt;span class="start-tag"&gt;div&lt;/span&gt;&amp;gt;29.05.2012&amp;lt;/&lt;span class="end-tag"&gt;div&lt;/span&gt;&amp;gt;&lt;br /&gt;      &amp;lt;/&lt;span class="end-tag"&gt;div&lt;/span&gt;&amp;gt;&lt;/pre&gt;&lt;pre id="line1"&gt;&amp;nbsp;&lt;/pre&gt;Таким образом, команды на скриншоте изменяют размер шрифта и цвет текста первой даты.&lt;br /&gt;&lt;br /&gt;Вообще, документация на ClojureScript достаточно бедная. Поэтому, вместо того, чтобы искать блог-посты и туториалы по использованию ClojureScript, я просто стал читать его исходники. Рекомендую для этого воспользоваться инструментом &lt;a href="https://github.com/fogus/marginalia"&gt;Marginalia&lt;/a&gt;. С Leiningen 2 это вообще просто.&lt;br /&gt;&lt;br /&gt;1. В .lein/profiles.clj добавьте плагин lein-marginalia&lt;br /&gt;&lt;br /&gt;{:user {:plugins [ [lein-marginalia "0.7.0"]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [lein-pprint "1.1.1"]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [lein-swank "1.4.4"]]}}&lt;br /&gt;&lt;br /&gt;2. Создайте новый проект: lein new clj-docs&lt;br /&gt;3. Скопируйте в катлог cljs-docs/src содержимое каталогоа cljorescript/src.&lt;br /&gt;4. Выполните команду lein marg, и в каталоге docs будет создан файл uberdoc.html, который слева содержит комментарии к исходнику,&amp;nbsp; а справа -- сам исходник.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-IDqzazd0N6c/T8Ca7GoMboI/AAAAAAAAANA/t7lCEJIzf2Y/s1600/doc.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="412" src="http://2.bp.blogspot.com/-IDqzazd0N6c/T8Ca7GoMboI/AAAAAAAAANA/t7lCEJIzf2Y/s640/doc.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Еще стоит ознакомиться с &lt;a href="http://closure-library.googlecode.com/svn/docs/index.html"&gt;Google Closure API&lt;/a&gt; -- тоже полезное подспорье в работе с ClojureScript.&lt;br /&gt;&lt;br /&gt;Ну а в целом, ClojureScript мне очень понравился, и я надеюсь использовать его дальше в своих проектах.&lt;br /&gt;&lt;br /&gt;&lt;pre id="line1"&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-7910071235775186798?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/7910071235775186798/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2012/05/clojurescript.html#comment-form' title='Комментарии: 3'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/7910071235775186798'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/7910071235775186798'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2012/05/clojurescript.html' title='Впечатления о ClojureScript'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-qQT9BLeDkDM/T8CYs2ii6HI/AAAAAAAAAM4/PAm5KuBpA7I/s72-c/clojurescript.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-5460356322053006814</id><published>2012-05-08T16:55:00.001+03:00</published><updated>2012-05-08T17:04:35.189+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='clojure'/><title type='text'>Гусеничный Lisp-трактор</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Просматривая записи Казимира Майорника, нашел у него очень любопытную &lt;a href="http://kazimirmajorinc.blogspot.com/2009/04/crawler-tractor.html"&gt;статью&lt;/a&gt;. Это всего лишь забавный эксперимент, показывающий, как при помощи лисповой парадигмы "код-как-данные" можно реализовать циклическое выполнение кода без циклов и рекурсий. Никакой практической ценности в этом нет, просто красиво смотрится. Я написал что-то похожее на Clojure, но несколько усовершенствованное. В моей версии алгоритм выглядит так.&lt;br /&gt;&lt;br /&gt;1) Список f содержит всего одну функцию. Её и запускаем первой.&lt;br /&gt;2) Первая функция передает свой код в update.&lt;br /&gt;3) Update "препарирует" код, находит номер текущей итерации, инкрементирует его и собирает такую же функцию, как ей передали, но с новым значением итерации.&lt;br /&gt;4) Первая функция получает вторую функцию и добавляет её в конец списка f. Если список f содержит более трех элементов, то первый элемент списка f удаляется. Таким образом, в списке f содержится не более четырех элементов.&lt;br /&gt;5) Первая функция запускает вторую функцию и завершается.&lt;br /&gt;&lt;br /&gt;Выглядит и впрямь, как гусеничный трак :-)&lt;br /&gt;&lt;br /&gt;&lt;div style="overflow: auto;"&gt;&lt;pre style="background: #ffffff; color: black;"&gt;&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;defn&lt;/span&gt; update &lt;span style="color: #808030;"&gt;[&lt;/span&gt;func&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;let&lt;/span&gt; &lt;span style="color: #808030;"&gt;[&lt;/span&gt;p &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;nth&lt;/span&gt; func &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;        &lt;span style="color: dimgrey;"&gt;;; Получить список (println (str "Hi for the " 1 " time!"))&lt;/span&gt;&lt;br /&gt;        n &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;nth&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;second&lt;/span&gt; p&lt;span style="color: #808030;"&gt;)&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;  &lt;span style="color: dimgrey;"&gt;;; Получить 1 из списка&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: dimgrey;"&gt;;; Создать список (println (str "Hi for the " 2 " time!"))&lt;/span&gt;&lt;br /&gt;        new-p &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;cons&lt;/span&gt; &lt;span style="color: purple;"&gt;'println&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;list&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;map&lt;/span&gt; #&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;if&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;=&lt;/span&gt; n &lt;span style="color: #808030;"&gt;%&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;inc&lt;/span&gt; n&lt;span style="color: #808030;"&gt;)&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;identity&lt;/span&gt; &lt;span style="color: #808030;"&gt;%&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;second&lt;/span&gt; p&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: dimgrey;"&gt;;; Заменить в теле функции (println (str "Hi for the " 1 " time!"))&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: dimgrey;"&gt;;; на (println (str "Hi for the " 2 " time!"))&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;map&lt;/span&gt; #&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;if&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;and&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;seq?&lt;/span&gt; &lt;span style="color: #808030;"&gt;%&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;=&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;first&lt;/span&gt; &lt;span style="color: #808030;"&gt;%&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt; &lt;span style="color: purple;"&gt;'println&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt; new-p &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;identity&lt;/span&gt; &lt;span style="color: #808030;"&gt;%&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt; func&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;def&lt;/span&gt; f &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;ref&lt;/span&gt; &lt;span style="color: #808030;"&gt;[&lt;/span&gt;&lt;span style="color: purple;"&gt;'&lt;/span&gt;&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;fn&lt;/span&gt; &lt;span style="color: #808030;"&gt;[&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;br /&gt;                &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;println&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;str&lt;/span&gt; &lt;span style="color: #0000e6;"&gt;"Hi for the "&lt;/span&gt; &lt;span style="color: #008c00;"&gt;1&lt;/span&gt; &lt;span style="color: #0000e6;"&gt;" time!"&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;                &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;let&lt;/span&gt; &lt;span style="color: #808030;"&gt;[&lt;/span&gt;func &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;last&lt;/span&gt; @f&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;                      &lt;span style="color: dimgrey;"&gt;;; Обновить список функций f&lt;/span&gt;&lt;br /&gt;                      &lt;span style="color: dimgrey;"&gt;;; Удалить первую функцию, если список f содержит больше трех элементов&lt;/span&gt;&lt;br /&gt;                      new-f &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;if&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;count&lt;/span&gt; @f&lt;span style="color: #808030;"&gt;)&lt;/span&gt; &lt;span style="color: #008c00;"&gt;3&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;                              &lt;span style="color: dimgrey;"&gt;;; Добавить обновленную функцию в конец списка&lt;/span&gt;&lt;br /&gt;                              &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;conj&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;vec&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;drop&lt;/span&gt; &lt;span style="color: #008c00;"&gt;1&lt;/span&gt; @f&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;update&lt;/span&gt; func&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt; &lt;br /&gt;                              &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;conj&lt;/span&gt; @f &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;update&lt;/span&gt; func&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;br /&gt;                  &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;dosync&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;ref-set&lt;/span&gt; f new-f&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;                  &lt;span style="color: dimgrey;"&gt;;; Выполнить последнюю функцию списка&lt;/span&gt;&lt;br /&gt;                  &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;eval&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;last&lt;/span&gt; @f&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;eval&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;last&lt;/span&gt; @f&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-5460356322053006814?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/5460356322053006814/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2012/05/lisp.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/5460356322053006814'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/5460356322053006814'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2012/05/lisp.html' title='Гусеничный Lisp-трактор'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-2686848577588213909</id><published>2012-05-07T15:03:00.002+03:00</published><updated>2012-05-07T15:11:10.148+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><title type='text'>CEDET 1.1, Emacs и Java</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;style type="text/css"&gt; &lt;!--   @page { margin: 0.79in }   P { margin-bottom: 0.08in }  --&gt;  &lt;/style&gt;  &lt;br /&gt;&lt;div style="margin-bottom: 0in;"&gt;Недавно вышедший Cedet 1.1 добавляет долгожданную поддержку импорта символов из jar-ок. Теперь Emacs предоставляет поддержку автодополнения кода java примерно на том же уровне, что и современные IDE.  &lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Например, у нас есть класс My1:&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-m9Mdc8sb6F4/T6e48hCSKvI/AAAAAAAAAME/AiG7XTvUAag/s1600/0.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-m9Mdc8sb6F4/T6e48hCSKvI/AAAAAAAAAME/AiG7XTvUAag/s1600/0.png" /&gt;&amp;nbsp;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;style type="text/css"&gt; &lt;!--   @page { margin: 0.79in }   P { margin-bottom: 0.08in }  --&gt;  &lt;/style&gt;  &lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Он используется в My2:&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-J5nWvEQFs-Y/T6e5DJpHbPI/AAAAAAAAAMM/7c7joWq7dz0/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-J5nWvEQFs-Y/T6e5DJpHbPI/AAAAAAAAAMM/7c7joWq7dz0/s1600/1.png" /&gt;&amp;nbsp;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;style type="text/css"&gt; &lt;!--   @page { margin: 0.79in }   P { margin-bottom: 0.08in }  --&gt;  &lt;/style&gt;  &lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Обратите внимание, как flymake параллельно скомпилировал файл и подсветил ошибки. Если набрать “my.” и выполнить функцию semantic-ia-complete-symbol-menu (у меня она замаплена на Ctrl-Space), то Emacs выдаст такую подсказку:&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-N3FZltjj6s0/T6e5KQjC8oI/AAAAAAAAAMU/40aSSsboFCE/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-N3FZltjj6s0/T6e5KQjC8oI/AAAAAAAAAMU/40aSSsboFCE/s1600/2.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;style type="text/css"&gt; &lt;!--   @page { margin: 0.79in }   P { margin-bottom: 0.08in }  --&gt;  &lt;/style&gt;  &lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Аналогично можно запрашивать подсказку по членам системных классов. Например, для StringBuilder:&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-eOZw9DGvgrI/T6e5TUCA41I/AAAAAAAAAMc/3SFjnqY4OWc/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-eOZw9DGvgrI/T6e5TUCA41I/AAAAAAAAAMc/3SFjnqY4OWc/s1600/3.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;style type="text/css"&gt; &lt;!--   @page { margin: 0.79in }   P { margin-bottom: 0.08in }  --&gt;  &lt;/style&gt;  &lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Правда надо учитывать, что, во-первых, импорты “звёздочкой” не срабатывают, системных классов semanticdb-javap тоже не знает. Поэтому, если необходимы подсказки, то импорт классов из неймспейса java.lang нужно делать явно, как в моем примере.&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Для того, чтобы заработали показанные выше механизмы, необходимо следующее:&lt;/div&gt;&lt;ol&gt;&lt;li&gt;&lt;div style="margin-bottom: 0in;"&gt;установленный пакет  flymake;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div style="margin-bottom: 0in;"&gt;установленный пакет  auto-complete;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div style="margin-bottom: 0in;"&gt;скачанный CEDET 1.1;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div style="margin-bottom: 0in;"&gt;Ну и конечно же,  последний Emacs и java.&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Конфигурация CEDET в .emacs :&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;(load-file "~/cedet/common/cedet.el")&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;           ;; Подгрузить cedet&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;(global-semanticdb-minor-mode 1) &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;   ;; Инициализировать semanticdb&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;;; Загрузить поддержку авто-дополнения &lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;(semantic-load-enable-gaudy-code-helpers) &lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;(custom-set-variables &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ;; Инициализация переменных, указывающих, где&amp;nbsp;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ;; установлена java&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;. . .&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;'(cedet-java-jdk-root "/opt/jdk")&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&amp;nbsp;;; Сюда нужно добавить все jar-ки, из&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&amp;nbsp;;; которых необходимо импортировать символы&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;'(semanticdb-javap-classpath '("/opt/jdk/jre/lib/rt.jar"))&lt;/div&gt;. . . &lt;br /&gt;&lt;div style="margin-bottom: 0in;"&gt;)&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Конфигурация flymake:&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;(require 'flymake)&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;(add-hook 'java-mode-hook 'flymake-mode-on)&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;(defun my-java-flymake-init ()&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;(list "javac" (list (flymake-init-create-temp-buffer-copy&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;'flymake-create-temp-with-folder-structure))))&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;(add-to-list 'flymake-allowed-file-name-masks '("\\.java$" my-java-flymake-init flymake-simple-cleanup))&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;Конфигурация auto-complete:&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;(require 'auto-complete-config)&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;(add-to-list 'ac-dictionary-directories "~/.emacs.d/ac-dict")&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;(ac-config-default)&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-2686848577588213909?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/2686848577588213909/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2012/05/cedet-11-emacs-java.html#comment-form' title='Комментарии: 8'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/2686848577588213909'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/2686848577588213909'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2012/05/cedet-11-emacs-java.html' title='CEDET 1.1, Emacs и Java'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-m9Mdc8sb6F4/T6e48hCSKvI/AAAAAAAAAME/AiG7XTvUAag/s72-c/0.png' height='72' width='72'/><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-5041398139791653503</id><published>2012-03-29T11:59:00.000+03:00</published><updated>2012-03-29T11:59:25.025+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='общение'/><title type='text'>Итоги за месяц</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Давно не писал сюда из-за нехватки времени. Но вот, наконец, я достаточно освободился, чтобы набросать пару строк в блог.&lt;br /&gt;&lt;br /&gt;Со времени последней записи у меня произошло много интересных событий, и многое можно было бы описать здесь, т.к. почти все так или иначе связано с Clojure. Наиболее интересное, на мой взгляд, -- это первое в моей практике применение Clojure на коммерческом проекте. О нем вкратце и расскажу.&lt;br /&gt;&lt;br /&gt;Проект, который я делал с начала февраля по середину марта, -- андроид-приложение для планшетов. Оно предназначалось для дистанционного обучения какой-то фигне сотрудников какой-то компании. Технических сложностей там никаких нету, писать здесь не о чем, за исключением достаточного большого объема работы: необходимо было реализовать 22 формы со всякими разными украшениями. Дополнительная сложность -- необходимо написать еще и JSON веб-сервис, который может отдавать запрашиваемые данные и сохранять у себя ответы пользователей на опросы и тесты.&lt;br /&gt;&lt;br /&gt;Дедлайн был очень жесткий: 12 марта. На мой взгляд, при таких объемах и сроках проект должен делать не один человек, а как минимум трое.&lt;br /&gt;&lt;br /&gt;Во-первых, мне удалось уговорить заказчика разрешить писать веб-сервис на Clojure вместо java. По моим прикидкам, это сэкономило бы мне неделю работы. Для заказчика были крайне важны сроки, и он позитивно воспринял мое предложение, но поинтересовался: какие минусы у clojure? Я честно сказал, что этот язык просто менее распространен, чем java. Все остальное -- то же, что и у java. Например, готовый сервис мы захостим под Tomcat в виде war-файла. Заказчик согласился с этим риском, заметив, что если вдальнейшем будет необходимость, то тогда вебсервис можно будет переделать на java. Забегая вперед скажу, что необходимость не возникла :-)&lt;br /&gt;&lt;br /&gt;Я воспользовался библиотеками noir1.3-alpha10, cheshire, sqlkorma, lobos и очень быстро показал работающий сервак. Функциональность в сервак я добавлял в процессе работы над клиентской частью по мере необходимости. Поскольку я пользовался связкой Emacs+Slime, рабочий код я получал очень быстро. А время, которое я тратил на разработку очередной функции сервака, в среднем равнялось нескольким минутам. &lt;br /&gt;&lt;br /&gt;Основное время, конечно же, занимала Android-часть, причем в основном -- настройка внешнего вида формочек. Android -- неплохая платформа для пользователя, но очень сырая и недоделанная для разработчика. В отличие от ябловодов, нам, андроидам, нет возможности пользоваться WYSIWYG-редактором для построения GUI. А редактировать гигантские простыни xml-кода вручную -- сомнительное удовольствие...&lt;br /&gt;&lt;br /&gt;Решение нашлось довольно быстро. Я разработал небольшой DSL на основе Clojure и Hiccup. С применением этого DSL формочку я целиком описывал на Clojure, а потом генерил из неё связку xml и java-кода (при этом генерился весь MVC на форму). В среднем, количество кода в DSL и количесство сгенеренного xml+java кода соотносились как 1:3 или 1:4, что уже очень неплохо. Еще один важный бонус, который я получил от этого DSL -- возможность использования нормального вменяемого языка для описания UI вместо XML. Появилась возможность выносить общую функциональность в функции, делать циклы и т.д. Понятно, что большинство повторно используемых компонент оформлялись как отдельные виджеты; но там, где для этого не было необходимости -- я просто объявлял функцию на Clojure. Еще важное замечание: код в стиле Hiccup читается гораздо проще, чем xml.&lt;br /&gt;&lt;br /&gt;Дальше развивать свой DSL у меня уже не было времени, а повторяющийся код, который я не мог обобщить, все равно встречался. Например, код для работы с БД и вебсервисом состоял по большей части из извлечения-записи в хеш-таблицы по ключам, соответствующим именам полей в БД. Здесь мне на помощь пришел Emacs. Буквально пара десятков строк кода на elisp-е, и весь оставшийся boilerplate-код на java генерил Emacs.&lt;br /&gt;&lt;br /&gt;Надо сказать, что в сроки я уложился, но с учетом, что в конце проекта на 2 недели мне дали в помощь товарища. Думаю, получилось очень даже хорошо.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-5041398139791653503?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/5041398139791653503/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2012/03/blog-post.html#comment-form' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/5041398139791653503'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/5041398139791653503'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2012/03/blog-post.html' title='Итоги за месяц'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-5189313045610543915</id><published>2012-02-19T11:35:00.000+03:00</published><updated>2012-02-19T11:35:03.244+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='noir'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Деплоймент Clojure под Tomcat</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Не так давно я взялся за небольшой проект с клиент-серверной архитектурой. JSON веб-сервис мне позволили реализовать на clojure, что я очень быстро и сделал при помощи библиотек: Noir, Lobos, Korma и Cheshire. Но зато была масса непоняток с деплойментом этого готового сервиса на хостинг. Во-первых, по непонятной причине, оригинальный сервер, поработав какое-то время, попросту отваливается. Я до сих теряюсь в догадках, что заставляет его так капризничать. Понятное дело, что я решил задеплоить всё на Tomcat. &lt;br /&gt;&lt;br /&gt;Мне не удалось нигде найти нормальной инструкции о том, как деплоить noir-приложение под томкат, так что я решил восполнить этот пробел.&lt;br /&gt;&lt;br /&gt;Данная инструкция рассчитана на веб-сайт, разработанный на clojure при помощи фреймворка Noir (опробовано на noir 1.3.0-alpha10).&lt;br /&gt;&lt;br /&gt;1) Добавьте [lein-ring "0.5.4"] в :dev-dependencies вашего файла project.clj.&lt;br /&gt;Внимание! [uk.org.alienscience/leiningen-war "0.0.13"] – это не то же самое, что lein-ring. Плагин lein-ring умеет создавать ring-handler для noir-приложения и запаковать все в war-ку. А плагин leiningen-war только создает war-ку, а ring-handler-а не создает.&lt;br /&gt;&lt;br /&gt;2) В файле project.clj укажите ваш ring-handler и пространство имен для ahead-of-time компиляции.&lt;br /&gt;:ring {:handler MyProject.server/handler} &lt;br /&gt;:aot [MyProject.server]&lt;br /&gt;&lt;br /&gt;3) Создайте ring-handler в server.clj:&lt;br /&gt;(:require [noir.server :as server])&lt;br /&gt;. . .&lt;br /&gt;(def handler (server/gen-handler {:base-url "/myproject"}))&lt;br /&gt;Внимание! Обязательно нужно указать :base-url, иначе редиректы и линки задеплоенного в томкат приложения будут указывать не на localhost:8080/myproject/, а на localhost:8080/ . Эпик фейл будет фееричный!&lt;br /&gt;&lt;br /&gt;4) Уберите такую загрузку вьюх: (server/load-views "src/MyProject/views/") из файла server.clj. Вьюхи нужно загружать явно:&amp;nbsp;&amp;nbsp; &lt;br /&gt;(:require [noir.server :as server]&lt;br /&gt;. . .&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [MyProject.views.pages])&lt;br /&gt;Если этого не сделать, то после упаковки в war-ку и даже jar-ку, ни один ваш маршрут работать не будет.&lt;br /&gt;&lt;br /&gt;5) Все линки и формы, если вы их генерите из hiccup, нужно создавать функциями:&lt;br /&gt;(hiccup.form-helpers/form-to [:post "/myurl"] ...) &lt;br /&gt;(hiccup.page-helpers/link-to "/myurl" …)&lt;br /&gt;Если этого не сделать, то они будут вести на localhost:8080/ вместо localhost:8080/myproject.&lt;br /&gt;&lt;br /&gt;6) Создайте war-ку командой lein ring uberwar. &lt;br /&gt;Внимание! Команда lein ring uberwar поставляется с lein-ring-плагином, и в нашем случае она работает. Команда lein uberwar поставляется с плагином&amp;nbsp; leiningen-war, и здесь она не работает. Так что этот плагин вообще лучше не использовать с noir.&lt;br /&gt;&lt;br /&gt;7) Скопируйте war-ку в webapps томката и переименуйте её в myproject.war. &lt;br /&gt;&lt;br /&gt;Теперь, после запуска томката, по адреусу localhost:8080/myproject будет ваше приложение. &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-5189313045610543915?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/5189313045610543915/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2012/02/clojure-tomcat.html#comment-form' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/5189313045610543915'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/5189313045610543915'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2012/02/clojure-tomcat.html' title='Деплоймент Clojure под Tomcat'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-1703369417605451593</id><published>2012-01-22T18:01:00.000+03:00</published><updated>2012-01-22T18:01:03.687+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='noir'/><title type='text'>Noir benchmark</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Решил немного потестить простое веб-приложение, описанное в предыдущей статье. Тесты проводились на ноутбуке Acer Aspire 5745G (Intel Core i5 2.26Ghz, 4Gb). Обнаружил следующее.&lt;br /&gt;&lt;br /&gt;Во-первых, при запуске сайта командой lein run запускаются два java-процесса. Первый, занимающий 28.8Mb -- видимо служебный, т.к. никакие его характерстики за время работы приложения не менялись. Второй, сходу запросивший 76.7Mb, -- само веб-приложение. Я создал два типа сущностей: mypage и mypage2 с CRUD-действиями для них, не используя БД. Т.е. скорость обработки запроса целиком зависила от скорости работы java-машины и не зависела от скорости ввода-вывода или поиска данных в таблицах. При обращении к различным действиям сущности mypage объем потребляемой памяти возрос до 90.3Mb. При обращении к mypage2 -- до 93.5Mb. При повторном обращении к этим же сущностям потребление памяти никак не поменялось. При перезапуске приложения цифры изменились до 96Mb и 99Mb.&lt;br /&gt;&lt;br /&gt;Интересно взглянуть на производительность веб-приложения под нагрузкой. Я создал скрипт, делающий 70000 таких запросов:&lt;br /&gt;wget http://localhost:8080/mypage&lt;br /&gt;wget http://localhost:8080/mypage/848867&lt;br /&gt;wget http://localhost:8080/mypage/edit/996283&lt;br /&gt;wget http://localhost:8080/mypage/527997&lt;br /&gt;wget http://localhost:8080/mypage/edit/410389&lt;br /&gt;&lt;br /&gt;Сущности с указанными айдишниками, естественно, существовали в приложении. Эти 70тыс. запросов jetty обработал за полчаса, отдавая, таким образом, примерно 2333 динамических страницы в минуту или примерно 39 в секунду.&lt;br /&gt;&lt;br /&gt;Самым интересным для меня в этом эксперименте стало потребление памяти и процессорного времени.&lt;br /&gt;&lt;br /&gt;Сначала jetty затребовал 92% процессорного времени, но его запросы довольно быстро сократились до 40%. Затем, довольно медленно, и они стали снижаться. К концу эксперимента jetty потреблял всего 4% процессорного времени.&lt;br /&gt;&lt;br /&gt;Аналогично, запросы памяти сначала были немалыми. Довольно быстро jetty "откушал" 232.8Mb, после чего потребляемая память стала уверенно снижаться, остановившись на отметке 84.6Mb.&lt;br /&gt;&lt;br /&gt;Надо еще учитывать, что нагрузка на сервер была ограничена возможностями консоли, в которой работал wget, ведь его вывод на экран тоже занимает какое-то время. Но, по крайней мере, верхнюю границу запросов процессора и памяти мы получили. Нижняя, естественно, достоверной не является.&lt;br /&gt;&lt;br /&gt;Такое снижение потребляемой памяти и процессора стало возможно благодаря run-time оптимизациям java-машины (в Sun некогда здорово над этим поработали). Вот и скажи теперь, что java -- медленная...&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-1703369417605451593?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/1703369417605451593/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2012/01/noir-benchmark.html#comment-form' title='Комментарии: 8'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/1703369417605451593'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/1703369417605451593'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2012/01/noir-benchmark.html' title='Noir benchmark'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-2950385532556177667</id><published>2012-01-08T12:22:00.001+03:00</published><updated>2012-01-08T12:53:00.703+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='noir'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>CRUD на Clojure средствами Noir</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;style type="text/css"&gt; &lt;!--   @page { size: 21cm 29.7cm; margin: 2cm }   P { margin-bottom: 0.21cm }   A:link { color: #000080; so-language: zxx; text-decoration: underline }  --&gt;  &lt;/style&gt;   &lt;br /&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Давно собирался рассказать немного о Noir – классном веб-фреймворке для Clojure. Наконец, у меня появилась возможность написать большой туториал по применению Noir, что я с удовольствием и сделаю :-)&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Есть ли вообще необходимость в самоучителе по Noir? Не очень существенная, если честно. На сайте фреймворка (webnoir.org) более чем приличная документация: раздел для быстрого старта, несколько обучалок по использованию Noir и даже индекс API. Мой туториал, возможно, будет вам полезен, если вы, зная Clojure и разобравшись более-менее с Noir, все еще не очень представляете себе архитектуру своего будущего приложения.&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;А задуматься над архитектурой придется. Noir очень сильно отличается от всех известных мне фреймворков. При своем минимализме он оставляет выбор конкретных библиотек за пользователем – такая свобода может привести к неверному их использованию. Насколько этот выбор широк можете судить хотя бы по &lt;a href="http://brehaut.net/blog/2011/ring_introduction"&gt;статье&lt;/a&gt; с обзором разных компонентов для каждого из уровней веб-стека на clojure. &lt;br /&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Давайте посмотрим, на что похоже приложение Noir. Если вы проследовали инструкциям из Getting Started на сайте webnoir.org, то у вас уже установлен leiningen-плагин для Noir. Создадим новый проект командой  lein noir new optimalist. У него очень простая структура.  &lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;b&gt;resources&lt;/b&gt; – содержит каталог public со статическим контентом (css, javasript, images);&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;b&gt;src&lt;/b&gt; – каталог с исходниками;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;b&gt;test&lt;/b&gt; – тесты;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;b&gt;project&lt;/b&gt;.clj – описание проекта и версий подключаемых библиотек.&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Исходники содержаться в двух каталогах &lt;b&gt;src/optimalist/views &lt;/b&gt;и&lt;b&gt; src/optimalist/models&lt;/b&gt;. Никаких вопросов не вызывает? А как насчет &lt;b&gt;src/optimalist/controllers&lt;/b&gt;? Такого каталога нету, т.&amp;nbsp;к.  самописных контроллеров здесь не предусмотрено. Это, конечно, у многих может вызвать разрыв шаблона: как вообще программировать MVC без C!  &lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Автор Noir Крис Гранжер очень подробно &lt;a href="http://groups.google.com/group/clj-noir/browse_thread/thread/1718a9b1312156d3/502c3c7e24f7319c?hl=ru&amp;amp;lnk=gst&amp;amp;q=controller#502c3c7e24f7319c"&gt;обосновал&lt;/a&gt; свое представление архитектуры. Исторически сложилось, что бизнес-логику обычно помещали в контроллер. Но со временем её все больше и больше выносили в отдельные компоненты, контроллер стал совсем тоненьким и, откровенно говоря, ненужным. Функции маршрутизации берет на себя фреймворк, так что в Noir (да и в других современных веб-фреймворках) строгой необходимости в отдельных контроллерах попросту нету. Единственное, что всё еще делает контроллер – это указывает, какую страницу рендерить в ответ на запрос пользователя. Раз уж код контроллера такой маленький, то Крис Гранжер попросту решил перенести функции контроллера на уровень View.  &lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Если сравнивать такой подход с другими фреймворками, то, пожалуй, Крис – один из первых, кто открыто решился заявить о ненужности контроллеров. Например, в практике программирования Ruby on Rails всё еще считается правильным всю бизнес логику вынести на уровень модели, а контроллеры сделать маленькими и легковесными. Это очень упрощает тестирование. Но это половинчатый подход: выбросили бы контроллеры да и дело с концом! Скажем, в JSF2.0 примерно так и обстоят дела. Вся бизнес-логика находится в Java-бинах, вызываются они с уровня View напрямую, минуя слой контроллера. Маршруты привязываются к страницам обычно централизованно в специальном файле конфигурации.  &lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Поскольку каталог models пустой, давайте взглянем повнимательнее на содержимое каталога views.  &lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;В файле common.clj создается специальная функция layout. Она задает общий шаблон страницы, заголовки и структуру html-файла.  &lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;В файле welcome.clj при помощи defpage задается маршрут «/welcome» и функция, которая сгенерирует ответ на запрос пользователя по этому маршруту. Как видно из кода, функция вернет строку приветствия. Шаблон html-страницы создается при помощи библиотеки Hiccup. Она позволяет писать html-код прямо на языке clojure при помощи родных для clojure структур данных: векторов и мапов.&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Hiccup очень удобен тем, что позволяет производить любые манипуляции с шаблоном прямо при помощи Clojure. Заметьте, такой подход вообще не встречается среди мейнстримных фреймворков. Что в ASP.NET, что в J2EE для разметки страницы применяется свой собственный DSL, построенный на базе XHTML. Как, например, сделать повторяющийся фрагмент страницы на JSF2.0, на которой еще есть экшены? Правильно, с применением ломика и какой-то матери. В ASP.NET с этим, вроде, несколько проще, но подход такой же: xml-теги. Ну и скажите мне, как программисту Java, почему я не могу использовать Java для изменения шаблона веб-страницы? Как ни крути, но даже Java... хотя нет, уж лучше этот долбаный XML, чем веб-страницы на Java...&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Короче, в Noir используется hiccup, а hiccup позволяет писать html-страницы на clojure в декларативном стиле.&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Вручную hiccup-код имеет смысл писать только для страниц очень небольшого размера. Если вам досталась от дизайнера монстрозная страница в десятки килобайт кода – конечно, такое поделие переписывать на hiccup не стоит. В этом вам поможет &lt;a href="https://github.com/nathell/clj-tagsoup"&gt;clj-tagsoup&lt;/a&gt;. Просто вызовите его функцию parse с указанием html-страницы и сохраните полученный hiccup-код.&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Почти все руководства о веб-фреймворках в виде первого учебного приложения предлагают свой вариант блога. Давайте и мы сделаем свой простой блог при помощи Noir. Переименуем welcome.clj в article_pages.clj (а также namespace) и напишем код первой страницы блога – списка записей. Еще я немного изменил список импортируемых библиотек.&lt;/div&gt;&lt;pre style="background: #f6f8ff; color: #000020;"&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;ns&lt;/span&gt; optimalist&lt;span style="color: #308080;"&gt;.&lt;/span&gt;views&lt;span style="color: #308080;"&gt;.&lt;/span&gt;article_pages&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:require&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;optimalist&lt;span style="color: #308080;"&gt;.&lt;/span&gt;views&lt;span style="color: #308080;"&gt;.&lt;/span&gt;common &lt;span style="color: #400000;"&gt;:as&lt;/span&gt; common&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: #308080;"&gt;[&lt;/span&gt;noir&lt;span style="color: #308080;"&gt;.&lt;/span&gt;response &lt;span style="color: #400000;"&gt;:as&lt;/span&gt; resp&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:use&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;noir&lt;span style="color: #308080;"&gt;.&lt;/span&gt;core&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #308080;"&gt;[&lt;/span&gt;hiccup&lt;span style="color: #308080;"&gt;.&lt;/span&gt;core&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defpage&lt;/span&gt; articles &lt;span style="color: #1060b6;"&gt;"/articles"&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;         &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;common/layout&lt;/span&gt;&lt;br /&gt;           &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:p&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"List of articles"&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;style type="text/css"&gt;&lt;!--   @page { size: 21cm 29.7cm; margin: 2cm }   P { margin-bottom: 0.21cm }  --&gt;  &lt;/style&gt;   &lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Обратите внимание, что маршрут «/articles» – именованный. В будущем я смогу получить его при помощи (url-for articles).  &lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Запустите веб-приложение командой lein run и откройте его по ссылке localhost:8080/articles. Разумеется, вы увидите текст «List of articles». Но пока что еще не работает корневой маршрут: localhost:8080/. Давайте добавим для него определение маршрута. &lt;/div&gt;&lt;pre style="background: #f6f8ff; color: #000020;"&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defpage&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"/"&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;resp/redirect&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;url-for&lt;/span&gt; articles&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;style type="text/css"&gt; &lt;!--   @page { size: 21cm 29.7cm; margin: 2cm }   P { margin-bottom: 0.21cm }  --&gt;&lt;/style&gt; Как видите, здесь объявляется неименованный маршрут «/», и вместо ответа на него сервер редиректит на страницу articles.&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Следующая проблема: как вывести список каких-либо элементов? Поскольку код шаблона мы пишем на clojure, то воспользуемся функцией for:&lt;/div&gt;&lt;pre style="background: #f6f8ff; color: #000020;"&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defpage&lt;/span&gt; articles &lt;span style="color: #1060b6;"&gt;"/articles"&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;let&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;data &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:id&lt;/span&gt; &lt;span style="color: #008c00;"&gt;1&lt;/span&gt;&lt;span style="color: #0066ee;"&gt;,&lt;/span&gt; &lt;span style="color: #400000;"&gt;:title&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"First title"&lt;/span&gt;&lt;span style="color: #0066ee;"&gt;,&lt;/span&gt; &lt;span style="color: #400000;"&gt;:body&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"First body"&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;br /&gt;              &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:id&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;span style="color: #0066ee;"&gt;,&lt;/span&gt; &lt;span style="color: #400000;"&gt;:title&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"Second title"&lt;/span&gt;&lt;span style="color: #0066ee;"&gt;,&lt;/span&gt; &lt;span style="color: #400000;"&gt;:body&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"Second body"&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;br /&gt;              &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:id&lt;/span&gt; &lt;span style="color: #008c00;"&gt;3&lt;/span&gt;&lt;span style="color: #0066ee;"&gt;,&lt;/span&gt; &lt;span style="color: #400000;"&gt;:title&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"Third title"&lt;/span&gt;&lt;span style="color: #0066ee;"&gt;,&lt;/span&gt; &lt;span style="color: #400000;"&gt;:body&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"Third body"&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;         &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;common/layout&lt;/span&gt;&lt;br /&gt;          &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:p&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"List of articles"&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;           &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;for&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;item data&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;             &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:div&lt;/span&gt;&lt;br /&gt;              &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:title&lt;/span&gt; item&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;              &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;              &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:body&lt;/span&gt; item&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;              &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;style type="text/css"&gt; &lt;!--   @page { size: 21cm 29.7cm; margin: 2cm }   P { margin-bottom: 0.21cm }  --&gt;&lt;/style&gt; Пока что все достаточно просто, не так ли? Страница articles покажет нам следующий текст:&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-style: normal; font-weight: normal;"&gt;&lt;span style="color: black;"&gt;&lt;span style="font-family: Times New Roman;"&gt;&lt;span style="font-size: small;"&gt;List of articles&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="border: none; font-style: normal; font-weight: normal; line-height: 0.64cm; margin-bottom: 0cm; orphans: 2; padding: 0cm; widows: 2;"&gt;&lt;span style="color: black;"&gt;&lt;span style="font-family: Times New Roman;"&gt;&lt;span style="font-size: small;"&gt;First title&lt;br /&gt;First body&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="border: none; font-style: normal; font-weight: normal; line-height: 0.64cm; margin-bottom: 0cm; orphans: 2; padding: 0cm; widows: 2;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="border: none; font-style: normal; font-weight: normal; line-height: 0.64cm; margin-bottom: 0cm; orphans: 2; padding: 0cm; widows: 2;"&gt;&lt;span style="color: black;"&gt;&lt;span style="font-family: Times New Roman;"&gt;&lt;span style="font-size: small;"&gt;Second title&lt;br /&gt;Second body&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="border: none; font-style: normal; font-weight: normal; line-height: 0.64cm; margin-bottom: 0cm; orphans: 2; padding: 0cm; widows: 2;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="border: none; font-style: normal; font-weight: normal; line-height: 0.64cm; margin-bottom: 0cm; orphans: 2; padding: 0cm; widows: 2;"&gt;&lt;span style="color: black;"&gt;&lt;span style="font-family: Times New Roman;"&gt;&lt;span style="font-size: small;"&gt;Third title&lt;br /&gt;Third body&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;Теперь данные нужно перенести в отдельный файл. Создадим в каталоге models файл article_model.clj, добавим туда код:&lt;br /&gt;&lt;pre style="background: #f6f8ff; color: #000020;"&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;ns&lt;/span&gt; optimalist&lt;span style="color: #308080;"&gt;.&lt;/span&gt;models&lt;span style="color: #308080;"&gt;.&lt;/span&gt;article_model&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;def&lt;/span&gt; *data*&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;ref&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:id&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"1"&lt;/span&gt;&lt;span style="color: #0066ee;"&gt;,&lt;/span&gt; &lt;span style="color: #400000;"&gt;:title&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"First article"&lt;/span&gt;&lt;span style="color: #0066ee;"&gt;,&lt;/span&gt; &lt;span style="color: #400000;"&gt;:body&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"Lorem ipsum dolor sit amet."&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:id&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"2"&lt;/span&gt;&lt;span style="color: #0066ee;"&gt;,&lt;/span&gt; &lt;span style="color: #400000;"&gt;:title&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"Second article"&lt;/span&gt;&lt;span style="color: #0066ee;"&gt;,&lt;/span&gt; &lt;span style="color: #400000;"&gt;:body&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"Lorem ipsum dolor sit amet."&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:id&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"3"&lt;/span&gt;&lt;span style="color: #0066ee;"&gt;,&lt;/span&gt; &lt;span style="color: #400000;"&gt;:title&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"Third article"&lt;/span&gt;&lt;span style="color: #0066ee;"&gt;,&lt;/span&gt; &lt;span style="color: #400000;"&gt;:body&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"Lorem ipsum dolor sit amet. "&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:id&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"4"&lt;/span&gt;&lt;span style="color: #0066ee;"&gt;,&lt;/span&gt; &lt;span style="color: #400000;"&gt;:title&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"Fourth article"&lt;/span&gt;&lt;span style="color: #0066ee;"&gt;,&lt;/span&gt; &lt;span style="color: #400000;"&gt;:body&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"Lorem ipsum dolor sit amet."&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defn&lt;/span&gt; fetch-list &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;  @*data*&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;style type="text/css"&gt; &lt;!--   @page { size: 21cm 29.7cm; margin: 2cm }   P { margin-bottom: 0.21cm }  --&gt;&lt;/style&gt; Воспользуемся этой моделью. Чтобы импортировать модель, добавим в секцию require такую строку: [optimalist.models.article_model :as article-model]; а код функции articles перепишем таким образом: &lt;/div&gt;&lt;pre style="background: #f6f8ff; color: #000020;"&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defpage&lt;/span&gt; articles &lt;span style="color: #1060b6;"&gt;"/articles"&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;common/layout&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:p&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"List of articles"&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;for&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;item &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;article-model/fetch-list&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;      &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:div&lt;/span&gt;&lt;br /&gt;       &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:title&lt;/span&gt; item&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;       &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;       &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:body&lt;/span&gt; item&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;       &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;style type="text/css"&gt; &lt;!--   @page { size: 21cm 29.7cm; margin: 2cm }   P { margin-bottom: 0.21cm }  --&gt;&lt;/style&gt; Вообще такая организация кода всё еще не очень удобная. Что если будет несколько страниц с похожим функционалом? Нам придется выносить hiccup-код в отдельные модули чтобы сделать его reusable. Давайте сделаем это сразу. Создатим файл view/article_templates.clj и вынесем туда генерацию httml.&lt;/div&gt;&lt;pre style="background: #f6f8ff; color: #000020;"&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;ns&lt;/span&gt; optimalist&lt;span style="color: #308080;"&gt;.&lt;/span&gt;views&lt;span style="color: #308080;"&gt;.&lt;/span&gt;article_templates&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:use&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;noir&lt;span style="color: #308080;"&gt;.&lt;/span&gt;core&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #308080;"&gt;[&lt;/span&gt;hiccup&lt;span style="color: #308080;"&gt;.&lt;/span&gt;core&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defn&lt;/span&gt; show-list &lt;span style="color: #308080;"&gt;[&lt;/span&gt;articles-list&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:p&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"List of articles"&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;for&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;item articles-list&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;      &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:div&lt;/span&gt;&lt;br /&gt;       &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:title&lt;/span&gt; item&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;       &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;       &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:body&lt;/span&gt; item&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;       &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;style type="text/css"&gt; &lt;!--   @page { size: 21cm 29.7cm; margin: 2cm }   P { margin-bottom: 0.21cm }  --&gt;  &lt;/style&gt;   &lt;/div&gt;В модуле article_pages.clj необходимо добавить строку [optimalist.views.article_templates :as article-view] в секцию :require. А страница articles тогда будет выглядеть так: &lt;br /&gt;&lt;pre style="background: #f6f8ff; color: #000020;"&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defpage&lt;/span&gt; articles &lt;span style="color: #1060b6;"&gt;"/articles"&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;common/layout&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;article-view/show-list&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;article-model/fetch-list&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Следующая наша задача – добавить к элементам списка ссылки на действия. Лично я – большой любитель технологии REST, о которой узнал из Ruby on Rails. Наши четыре действия над сущностью create, read, update, delete подразумевают наличие семи маршрутов:&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;table cellpadding="4" cellspacing="0" style="width: 642px;"&gt;&lt;colgroup&gt;&lt;col width="152"&gt;&lt;/col&gt;  &lt;col width="138"&gt;&lt;/col&gt;  &lt;col width="326"&gt;&lt;/col&gt;  &lt;/colgroup&gt;&lt;tbody&gt;&lt;tr valign="TOP"&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: 1px solid #000000; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0.1cm;" width="152"&gt;&lt;b&gt;Маршрут&lt;/b&gt;&lt;/td&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: 1px solid #000000; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0.1cm;" width="138"&gt;&lt;b&gt;Метод&lt;/b&gt;&lt;/td&gt;   &lt;td style="border: 1px solid #000000; padding: 0.1cm;" width="326"&gt;&lt;b&gt;Описание&lt;/b&gt;&lt;/td&gt;  &lt;/tr&gt;&lt;tr valign="TOP"&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="152"&gt;/articles&lt;/td&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="138"&gt;GET&lt;/td&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: 1px solid #000000; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0.1cm; padding-top: 0cm;" width="326"&gt;Список записей&lt;/td&gt;  &lt;/tr&gt;&lt;tr valign="TOP"&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="152"&gt;/article/:id&lt;/td&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="138"&gt;GET&lt;/td&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: 1px solid #000000; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0.1cm; padding-top: 0cm;" width="326"&gt;Просмотреть одну запись&lt;/td&gt;  &lt;/tr&gt;&lt;tr valign="TOP"&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="152"&gt;/article/edit/:id&lt;/td&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="138"&gt;GET&lt;/td&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: 1px solid #000000; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0.1cm; padding-top: 0cm;" width="326"&gt;Показать форму редактирования &lt;span style="font-weight: normal;"&gt;записи&lt;/span&gt;&lt;/td&gt;  &lt;/tr&gt;&lt;tr valign="TOP"&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="152"&gt;/article/update/:id&lt;/td&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="138"&gt;POST&lt;/td&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: 1px solid #000000; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0.1cm; padding-top: 0cm;" width="326"&gt;Изменить запись в соответствии с    указанными данными&lt;/td&gt;  &lt;/tr&gt;&lt;tr valign="TOP"&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="152"&gt;/article/delete/:id&lt;/td&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="138"&gt;POST&lt;/td&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: 1px solid #000000; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0.1cm; padding-top: 0cm;" width="326"&gt;Удалить запись&lt;/td&gt;  &lt;/tr&gt;&lt;tr valign="TOP"&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="152"&gt;/article/new&lt;/td&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="138"&gt;GET&lt;/td&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: 1px solid #000000; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0.1cm; padding-top: 0cm;" width="326"&gt;Показать пустую форму для создания    записи&lt;/td&gt;  &lt;/tr&gt;&lt;tr valign="TOP"&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="152"&gt;/article/create&lt;/td&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: none; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0cm; padding-top: 0cm;" width="138"&gt;&lt;div style="font-weight: normal;"&gt;POST&lt;/div&gt;&lt;/td&gt;   &lt;td style="border-bottom: 1px solid #000000; border-left: 1px solid #000000; border-right: 1px solid #000000; border-top: none; padding-bottom: 0.1cm; padding-left: 0.1cm; padding-right: 0.1cm; padding-top: 0cm;" width="326"&gt;Создать запись в соответствии с    указанными данными&lt;/td&gt;  &lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Вообще в REST должны использоваться методы не только GET и POST, а также PUT (для создания новой записи) и DELETE (для удаления). Но то ли браузеры не понимают этих методов, то ли веб сервера, но факт остается фактом: использовать мы можем только GET и POST.  В Ruby on Rails для обхода этого ограничения создается скрытое поле. И если указывается метод PUT или DELETE, то на самом деле используется POST с пометкой в скрытом поле, что это все-таки PUT/DELETE.&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Разные методы отправки запроса нужно использовать не только для красоты. По сети ползают роботы-индексаторы, которые пытаются перейти по ссылкам на другие страницы. Представьте, например, что на вашем сайте кнопка DELETE сделана при помощи обыкновенной ссылки (запрос методом GET), и робот решит по ней перейти.  Робот ведь не знает, что эта ссылка приводит к какому-то разрушительному действию, правда? Вот для этого-то все запросы, как-нибудь изменяющие сущность, должны быть отправлены методом POST.&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Итак, первая ссылка, которую я хотел бы добавить к списку сущностей – маршрут /article/:id для просмотра записи.&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;Вначале в article_pages добавим страницу: &lt;br /&gt;&lt;pre style="background: #f6f8ff; color: #000020;"&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defpage&lt;/span&gt; view-article &lt;span style="color: #1060b6;"&gt;"/article/:id"&lt;/span&gt; &lt;span style="color: #308080;"&gt;{&lt;/span&gt;id &lt;span style="color: #400000;"&gt;:id&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;common/layout&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #1060b6;"&gt;"View article"&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;style type="text/css"&gt; &lt;!--   @page { size: 21cm 29.7cm; margin: 2cm }   P { margin-bottom: 0.21cm }  --&gt;  &lt;/style&gt;   &lt;br /&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Она пока ничего не делает, но зато её сразу же можно протестить в браузере по маршруту /article/1. Заметьте, что в определении страницы задан хеш параметров {id :id}. Это деструктуризаця параметров запроса, и делает она именно то, на что похоже: создает локальную переменную id, значение которой будет получено из части маршрута :id.&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Теперь добавим ссылку на эту страницу в функции show-list&lt;/div&gt;&lt;pre style="background: #f6f8ff; color: #000020;"&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defn&lt;/span&gt; show-list &lt;span style="color: #308080;"&gt;[&lt;/span&gt;articles-list&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:p&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"List of articles"&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;for&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;item articles-list&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;      &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:div&lt;/span&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:title&lt;/span&gt; item&lt;span style="color: #308080;"&gt;)&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;       &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:body&lt;/span&gt; item&lt;span style="color: #308080;"&gt;)&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;       &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:a&lt;/span&gt; &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:href&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;url-for&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;str&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"/article/"&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:id&lt;/span&gt; item&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"View"&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;       &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;/pre&gt;Функция url-for умеет создавать абсолютный URL по заданным параметрам. Параметром может быть как текстовая строка с маршрутом, так и сам именованный маршрут. И хотя предложенное выше решение – вполне работоспособное, но оно все-таки не оптимальное. Что если вы решите изменить URL в странице view-article? Тогда вам придется искать все места, где вы на него ссылаетесь, и править маршруты вручную. Лучше использовать именованый маршрут следующим образом: &lt;br /&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;(url-for optimalist.views.article_pages/view-article {:id (:id item)})&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Здесь optimalist.views.article_pages/view-article  – полное имя именованного маршрута. Необходимо писать его целиком, т.&amp;nbsp;к. в файле article_templates.clj ничего не известно о aricle_pages.clj. И импортировать article_pages.clj мы тоже не можем, т.&amp;nbsp;к. там уже есть импорт article_templates.clj, и мы получим циклическую зависимость. Поэтому, все-таки, придется писать целиком.&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Второй параметр – хеш значений, которые будут переданы в генератор маршрута. Поскольку наш маршрут выглядит как «/article/:id», то ему нужно передать хеш {:id &amp;lt;значение&amp;gt;}. &lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Вообще, конечно же, такая запись жутко громоздкая и неудобная. Поэтому в начало файла article_templates.clj, после объявления неймспейса, добавим алиас:&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;(alias 'article 'optimalist.views.article_pages)&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Теперь генерацию ссылки можно переписать таким образом:&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;(url-for article/view-article {:id (:id item)})&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;Это уже значительно лучше, но все еще пока не идеально. Элементов типа «ссылка» у нас будет достаточно много, особенно, если много разных сущностей. Но типов ссылок всего несколько: new, view, edit, delete, back. Мы могли бы сделать более абстрактный механизм генерации ссылок, который инкапсулирует в себе всю внутреннюю структуру вектора [:a …], в том числе и надпись на ссылке. Создадим файл views/helpers/widget.clj:&amp;nbsp; &lt;br /&gt;&lt;pre style="background: #f6f8ff; color: #000020;"&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;ns&lt;/span&gt; optimalist&lt;span style="color: #308080;"&gt;.&lt;/span&gt;views&lt;span style="color: #308080;"&gt;.&lt;/span&gt;helpers&lt;span style="color: #308080;"&gt;.&lt;/span&gt;widget&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:require&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;noir&lt;span style="color: #308080;"&gt;.&lt;/span&gt;core&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defmacro&lt;/span&gt; view-link &lt;span style="color: #308080;"&gt;[&lt;/span&gt;page id&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #406080;"&gt;`&lt;/span&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;vec&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:a&lt;/span&gt;&lt;br /&gt;     &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:href&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;noir.core/url-for&lt;/span&gt; &lt;span style="color: #308080;"&gt;~&lt;/span&gt;page &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:id&lt;/span&gt; &lt;span style="color: #308080;"&gt;~&lt;/span&gt;id&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"View"&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;style type="text/css"&gt; &lt;!--   @page { size: 21cm 29.7cm; margin: 2cm }   P { margin-bottom: 0.21cm }  --&gt;&lt;/style&gt; Теперь его можно подключить в article_templates.clj, добавив строку [optimalist.views.helpers.widget] в секцию :use. А вместо записи [:a {href … } «View»] вставим вызов макроса: (view-link article/view-article (:id item)).&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Макрос здесь нужен из-за параметра page – символа, к которому привязан именованный маршрут. Конечно, его можно было бы передавать закавыченным, но это не очень-то красиво.&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Сам макрос пока-что не слишком удачный. Что если нам понадобится добавить еще несколько похожих ссылок? Copy-paste – не наш метод, поэтому немного расширим наш язык для данного конкретного случая.  &lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Это очень интересная технология, описанная Полом Грэмом в книге «On Lisp», и называется она «bottom-up programming». Идея заключается в том, что вы не только пишете свое приложение, используя Lisp, но и изменяете Lisp, чтобы легче было писать ваше предложение. Снизу, в язык вводятся новые конструкции, упрощающие реализацию приложения. Сверху, приложение описывается при помощи ваших новых конструкций, что делает код значительно короче. Отсюда и bottom-up programming.&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;Новые конструкции, которые мы вводим в язык, называются утилитами. Давайте добавим  две таких: &lt;br /&gt;&lt;pre style="background: #f6f8ff; color: #000020;"&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defmacro&lt;/span&gt; common-url &lt;span style="color: #308080;"&gt;[&lt;/span&gt;page id&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #406080;"&gt;`&lt;/span&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;noir.core/url-for&lt;/span&gt; &lt;span style="color: #308080;"&gt;~&lt;/span&gt;page &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:id&lt;/span&gt; &lt;span style="color: #308080;"&gt;~&lt;/span&gt;id&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defmacro&lt;/span&gt; common-link &lt;span style="color: #308080;"&gt;[&lt;/span&gt;url text &lt;span style="color: #308080;"&gt;&amp;amp;&lt;/span&gt; params&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #406080;"&gt;`&lt;/span&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;vec&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:a&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;merge&lt;/span&gt; &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:href&lt;/span&gt; &lt;span style="color: #308080;"&gt;~&lt;/span&gt;url&lt;span style="color: #308080;"&gt;}&lt;/span&gt; &lt;span style="color: #308080;"&gt;~&lt;/span&gt;@params&lt;span style="color: #308080;"&gt;)&lt;/span&gt; &lt;span style="color: #308080;"&gt;~&lt;/span&gt;text&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;style type="text/css"&gt; &lt;!--   @page { size: 21cm 29.7cm; margin: 2cm }   P { margin-bottom: 0.21cm }  --&gt;&lt;/style&gt; Первый макрос просто инкапсулирует знание о том, как создавать URL. Теперь нам не надо заботиться о том, чтобы дописывать noir.core к url-for, а также создавать хеш для единственного занчения id.&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;Второй макрос создает html-ссылку с заданным текстом на указанный URL. Необязательный параметр params – это возможные параметры самой ссылки. Например, если нам потребуется задать обработчик onclick, то мы сможем сделать это через params.&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;Перепишем текст генерации ссылки с применением двух новых утилит: &lt;br /&gt;&lt;pre style="background: #f6f8ff; color: #000020;"&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defmacro&lt;/span&gt; view-link &lt;span style="color: #308080;"&gt;[&lt;/span&gt;page id&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #406080;"&gt;`&lt;/span&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;common-link&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;common-url&lt;/span&gt; &lt;span style="color: #308080;"&gt;~&lt;/span&gt;page &lt;span style="color: #308080;"&gt;~&lt;/span&gt;id&lt;span style="color: #308080;"&gt;)&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"View"&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defmacro&lt;/span&gt; edit-link &lt;span style="color: #308080;"&gt;[&lt;/span&gt;page id&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #406080;"&gt;`&lt;/span&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;common-link&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;common-url&lt;/span&gt; &lt;span style="color: #308080;"&gt;~&lt;/span&gt;page &lt;span style="color: #308080;"&gt;~&lt;/span&gt;id&lt;span style="color: #308080;"&gt;)&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"Edit"&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;style type="text/css"&gt; &lt;!--   @page { size: 21cm 29.7cm; margin: 2cm }   P { margin-bottom: 0.21cm }  --&gt;  &lt;/style&gt;   &lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&amp;nbsp;Итак, на данный момент, у нас есть список записей и ссылка на просмотр каждой записи. Давайте теперь реализуем функцию view-article: &lt;/div&gt;&lt;pre style="background: #f6f8ff; color: #000020;"&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defpage&lt;/span&gt; view-article &lt;span style="color: #1060b6;"&gt;"/article/:id"&lt;/span&gt; &lt;span style="color: #308080;"&gt;{&lt;/span&gt;id &lt;span style="color: #400000;"&gt;:id&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;common/layout&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;article-view/show&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;article-model/fetch&lt;/span&gt; id&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;style type="text/css"&gt; &lt;!--   @page { size: 21cm 29.7cm; margin: 2cm }   P { margin-bottom: 0.21cm }  --&gt;&lt;/style&gt; Чтобы она заработала, нужно в article_templates добавить функцию show: &lt;/div&gt;&lt;pre style="background: none repeat scroll 0% 0% rgb(246, 248, 255); color: #000020;"&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defn&lt;/span&gt; show &lt;span style="color: #308080;"&gt;[&lt;/span&gt;article&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:div&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:h1&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:title&lt;/span&gt; article&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:div&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:body&lt;/span&gt; article&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:a&lt;/span&gt; &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:href&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;url-for&lt;/span&gt; article&lt;span style="color: #308080;"&gt;/&lt;/span&gt;articles&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"Back"&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;) &lt;/span&gt;&lt;/pre&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;В article_model необходимо добавить функцию fetch:&lt;span style="color: #308080;"&gt; &lt;/span&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;/div&gt;&lt;pre style="background: #f6f8ff; color: #000020;"&gt;&lt;span style="color: #308080;"&gt;&lt;/span&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defn&lt;/span&gt; fetch &lt;span style="color: #308080;"&gt;[&lt;/span&gt;id&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;first&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;filter&lt;/span&gt; #&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;=&lt;/span&gt; id &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:id&lt;/span&gt; &lt;span style="color: #308080;"&gt;%&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt; @*data*&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;style type="text/css"&gt; &lt;!--   @page { size: 21cm 29.7cm; margin: 2cm }   P { margin-bottom: 0.21cm }  --&gt;  &lt;/style&gt;   &lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;На этом, пожалуй, все сложности и закончились. Дальше, по накатанной. Проект целиком вы можете найти &lt;a href="https://github.com/dbushenko/Simple-Noir-blog-example"&gt;здесь&lt;/a&gt;, а вот как выглядят два основных файла проекта.&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;b&gt;article_pages.clj&lt;/b&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;/div&gt;&lt;pre style="background: #f6f8ff; color: #000020;"&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;ns&lt;/span&gt; optimalist&lt;span style="color: #308080;"&gt;.&lt;/span&gt;views&lt;span style="color: #308080;"&gt;.&lt;/span&gt;article_pages&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:require&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;optimalist&lt;span style="color: #308080;"&gt;.&lt;/span&gt;views&lt;span style="color: #308080;"&gt;.&lt;/span&gt;common &lt;span style="color: #400000;"&gt;:as&lt;/span&gt; common&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: #308080;"&gt;[&lt;/span&gt;noir&lt;span style="color: #308080;"&gt;.&lt;/span&gt;response &lt;span style="color: #400000;"&gt;:as&lt;/span&gt; resp&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: #308080;"&gt;[&lt;/span&gt;optimalist&lt;span style="color: #308080;"&gt;.&lt;/span&gt;views&lt;span style="color: #308080;"&gt;.&lt;/span&gt;article_templates &lt;span style="color: #400000;"&gt;:as&lt;/span&gt; article-view&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: #308080;"&gt;[&lt;/span&gt;optimalist&lt;span style="color: #308080;"&gt;.&lt;/span&gt;models&lt;span style="color: #308080;"&gt;.&lt;/span&gt;article_model &lt;span style="color: #400000;"&gt;:as&lt;/span&gt; article-model&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:use&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;noir&lt;span style="color: #308080;"&gt;.&lt;/span&gt;core&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defpage&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"/"&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;resp/redirect&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;url-for&lt;/span&gt; articles&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defpage&lt;/span&gt; articles &lt;span style="color: #1060b6;"&gt;"/articles"&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;common/layout&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;article-view/show-list&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;article-model/fetch-list&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defpage&lt;/span&gt; view-article &lt;span style="color: #1060b6;"&gt;"/article/:id"&lt;/span&gt; &lt;span style="color: #308080;"&gt;{&lt;/span&gt;id &lt;span style="color: #400000;"&gt;:id&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;common/layout&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;article-view/show&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;article-model/fetch&lt;/span&gt; id&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defpage&lt;/span&gt; edit-article &lt;span style="color: #1060b6;"&gt;"/article/edit/:id"&lt;/span&gt; &lt;span style="color: #308080;"&gt;{&lt;/span&gt;id &lt;span style="color: #400000;"&gt;:id&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;let&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;item &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;article-model/fetch&lt;/span&gt; id&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;common/layout&lt;/span&gt;&lt;br /&gt;     &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;article-view/form&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;url-for&lt;/span&gt; update-article &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:id&lt;/span&gt; id&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"Update"&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:title&lt;/span&gt; item&lt;span style="color: #308080;"&gt;)&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:body&lt;/span&gt; item&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defpage&lt;/span&gt; update-article &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:post&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"/article/update/:id"&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt; &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:keys&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;id title body&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;article-model/update&lt;/span&gt; id title body&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;resp/redirect&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;url-for&lt;/span&gt; view-article &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:id&lt;/span&gt; id&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defpage&lt;/span&gt; new-article &lt;span style="color: #1060b6;"&gt;"/article/new"&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;common/layout&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;article-view/form&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;url-for&lt;/span&gt; create-article&lt;span style="color: #308080;"&gt;)&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"Create"&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defpage&lt;/span&gt; create-article &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:post&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"/article/create"&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt; &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:keys&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;title body&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;article-model/create&lt;/span&gt; title body&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;resp/redirect&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;url-for&lt;/span&gt; articles&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defpage&lt;/span&gt; delete-article &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:post&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"/delete/:id"&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt; &lt;span style="color: #308080;"&gt;{&lt;/span&gt;id &lt;span style="color: #400000;"&gt;:id&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;article-model/delete&lt;/span&gt; id&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;resp/redirect&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;url-for&lt;/span&gt; articles&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;b&gt;article_templates.clj&lt;/b&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;/div&gt;&lt;pre style="background: #f6f8ff; color: #000020;"&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;ns&lt;/span&gt; optimalist&lt;span style="color: #308080;"&gt;.&lt;/span&gt;views&lt;span style="color: #308080;"&gt;.&lt;/span&gt;article_templates&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:require&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;optimalist&lt;span style="color: #308080;"&gt;.&lt;/span&gt;views&lt;span style="color: #308080;"&gt;.&lt;/span&gt;helpers&lt;span style="color: #308080;"&gt;.&lt;/span&gt;widget &lt;span style="color: #400000;"&gt;:as&lt;/span&gt; widget&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:use&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;noir&lt;span style="color: #308080;"&gt;.&lt;/span&gt;core&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #308080;"&gt;[&lt;/span&gt;hiccup&lt;span style="color: #308080;"&gt;.&lt;/span&gt;core&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;alias&lt;/span&gt; &lt;span style="color: #406080;"&gt;'article&lt;/span&gt; &lt;span style="color: #406080;"&gt;'optimalist&lt;/span&gt;&lt;span style="color: #308080;"&gt;.&lt;/span&gt;views&lt;span style="color: #308080;"&gt;.&lt;/span&gt;article_pages&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defn&lt;/span&gt; show &lt;span style="color: #308080;"&gt;[&lt;/span&gt;article&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:div&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:h1&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:title&lt;/span&gt; article&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:div&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:body&lt;/span&gt; article&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:a&lt;/span&gt; &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:href&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;url-for&lt;/span&gt; article&lt;span style="color: #308080;"&gt;/&lt;/span&gt;articles&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"Back"&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defn&lt;/span&gt; list-item &lt;span style="color: #308080;"&gt;[&lt;/span&gt;item&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;let&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;id &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:id&lt;/span&gt; item&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:div&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:div&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:title&lt;/span&gt; item&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;     &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;:body&lt;/span&gt; item&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;     &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:form&lt;/span&gt; &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:id&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;str&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"form-"&lt;/span&gt; id&lt;span style="color: #308080;"&gt;)&lt;/span&gt; &lt;span style="color: #400000;"&gt;:method&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"POST"&lt;/span&gt;&lt;span style="color: #0066ee;"&gt;,&lt;/span&gt; &lt;span style="color: #400000;"&gt;:action&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;url-for&lt;/span&gt; article&lt;span style="color: #308080;"&gt;/&lt;/span&gt;delete-article &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:id&lt;/span&gt; id&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;br /&gt;      &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:div&lt;/span&gt;&lt;br /&gt;       &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;widget/view-link&lt;/span&gt; article&lt;span style="color: #308080;"&gt;/&lt;/span&gt;view-article id&lt;span style="color: #308080;"&gt;)&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;" | "&lt;/span&gt;&lt;br /&gt;       &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;widget/edit-link&lt;/span&gt; article&lt;span style="color: #308080;"&gt;/&lt;/span&gt;edit-article id&lt;span style="color: #308080;"&gt;)&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;" | "&lt;/span&gt;&lt;br /&gt;       &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;widget/delete-link&lt;/span&gt; id&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;     &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defn&lt;/span&gt; show-list &lt;span style="color: #308080;"&gt;[&lt;/span&gt;articles-list&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:div&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;for&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;item articles-list&lt;span style="color: #308080;"&gt;]&lt;/span&gt; &lt;br /&gt;     &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;list-item&lt;/span&gt; item&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:a&lt;/span&gt; &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:href&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;url-for&lt;/span&gt; article&lt;span style="color: #308080;"&gt;/&lt;/span&gt;new-article&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"New"&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;defn&lt;/span&gt; form &lt;span style="color: #308080;"&gt;[&lt;/span&gt;url button-text &lt;span style="color: #308080;"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color: #308080;"&gt;[&lt;/span&gt;title body &lt;span style="color: #308080;"&gt;&amp;amp;&lt;/span&gt; rest&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:form&lt;/span&gt; &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:method&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"POST"&lt;/span&gt;&lt;span style="color: #0066ee;"&gt;,&lt;/span&gt; &lt;span style="color: #400000;"&gt;:action&lt;/span&gt; url&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:input&lt;/span&gt; &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:name&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"title"&lt;/span&gt;&lt;span style="color: #0066ee;"&gt;,&lt;/span&gt; &lt;span style="color: #400000;"&gt;:type&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"text"&lt;/span&gt;&lt;span style="color: #0066ee;"&gt;,&lt;/span&gt; &lt;span style="color: #400000;"&gt;:value&lt;/span&gt; title&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:textarea&lt;/span&gt; &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:name&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"body"&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt; body&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:input&lt;/span&gt; &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:type&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"submit"&lt;/span&gt;&lt;span style="color: #0066ee;"&gt;,&lt;/span&gt; &lt;span style="color: #400000;"&gt;:value&lt;/span&gt; button-text&lt;span style="color: #308080;"&gt;}&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:br&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #308080;"&gt;[&lt;/span&gt;&lt;span style="color: #400000;"&gt;:a&lt;/span&gt; &lt;span style="color: #308080;"&gt;{&lt;/span&gt;&lt;span style="color: #400000;"&gt;:href&lt;/span&gt; &lt;span style="color: #308080;"&gt;(&lt;/span&gt;&lt;span style="color: #200080; font-weight: bold;"&gt;url-for&lt;/span&gt; article&lt;span style="color: #308080;"&gt;/&lt;/span&gt;articles&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;span style="color: #308080;"&gt;}&lt;/span&gt; &lt;span style="color: #1060b6;"&gt;"Back"&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;]&lt;/span&gt;&lt;span style="color: #308080;"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;/div&gt;&lt;div style="font-weight: normal; margin-bottom: 0cm;"&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-2950385532556177667?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/2950385532556177667/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2012/01/crud-clojure-noir.html#comment-form' title='Комментарии: 3'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/2950385532556177667'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/2950385532556177667'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2012/01/crud-clojure-noir.html' title='CRUD на Clojure средствами Noir'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-7607463103719360442</id><published>2012-01-02T12:20:00.000+03:00</published><updated>2012-01-02T12:20:16.985+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><title type='text'>my-project.el</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;При использовании Emacs мне очень сильно не хватает какого-нибудь простого средства управления проектами. Наиболее важные для меня функции:&lt;br /&gt;1) Быстрый поиск по файлам проекта с автодополнением имени;&lt;br /&gt;2) Запуск etags только по файлам проекта;&lt;br /&gt;3) Поиск по содержимому файлов проекта.&lt;br /&gt;&lt;br /&gt;Среди опробованных стандартных решений не нашлось ни одного, подходящего для меня. Поэтому я решил написать свой &lt;a href="https://github.com/dbushenko/my-project"&gt;плагин&lt;/a&gt;, которым и хочу здесь поделиться.&lt;br /&gt;&lt;br /&gt;Вот как я объявляю проект:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-owazSt0_OSo/TwFzQk_vhHI/AAAAAAAAAJg/iJqepjBL9h4/s1600/00.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="187" src="http://2.bp.blogspot.com/-owazSt0_OSo/TwFzQk_vhHI/AAAAAAAAAJg/iJqepjBL9h4/s640/00.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Эта функция создает проект "My", в котором объявлен корневой каталог проекта, несколько артефактов, расширение файлов с исходниками, по которым будет работать etags; в опции find-filе задаются маски файлов для быстрого поиска; в опции exclude указываются строки, при наличии которых файлы исключаются из поиска. Последняя опция удобна тем, что помогает исключить из результатов поиска различные автоматически генерируемые файлы или файлы из каталогa .svn.&lt;br /&gt;&lt;br /&gt;Быстрый поиск хотелось сделать как в TextMate. Что-то похожее в Emacs реализуется средствами IDO, но работает не совсем так, как надо. Во-первых, IDO подглючивает при большой вложенности каталогов и большом количестве файлов; во-вторых, find-file ищет от текущего каталога. Если вы находитесь в самой глубине вашего проекта, то для поиска файла в другом каталоге, нужно будет выходить на несколько уровней выше. Я сделал функцию поиска, которая работает от корня проекта и выводит в подсказку весь список всех файлов проекта.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-7CCorz6ca4o/TwF02orOj6I/AAAAAAAAAJs/Ip67NM9S8oM/s1600/01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="192" src="http://3.bp.blogspot.com/-7CCorz6ca4o/TwF02orOj6I/AAAAAAAAAJs/Ip67NM9S8oM/s640/01.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Если набрать дополнительно несколько букв имени искомого файла, IDO поможет отфильтровать список.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-s_pcCwRheyc/TwF1IwpQN7I/AAAAAAAAAJ4/ck6yU-7sbAE/s1600/02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="193" src="http://4.bp.blogspot.com/-s_pcCwRheyc/TwF1IwpQN7I/AAAAAAAAAJ4/ck6yU-7sbAE/s640/02.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Еще одна не очень удобная для меня функция Emacs -- поиск по содержимому файлов. Для этого используется стандартная функция find-grep.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-mvV6PNN3QSQ/TwF1bNckvII/AAAAAAAAAKE/mXNPNyigQ_Q/s1600/06.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="192" src="http://1.bp.blogspot.com/-mvV6PNN3QSQ/TwF1bNckvII/AAAAAAAAAKE/mXNPNyigQ_Q/s640/06.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Как видите, при запуске этой функии Emacs выдает подсказку по параметрам команды find, которые нужно заполнить вручную. Самое отвратительное в этом -- это необходимость вручную указывать имя корневой дирректории и шаблона имени файла.&lt;br /&gt;&lt;br /&gt;Я сделал свою версию этой функции. Вначале, она спросит, по каким файлам проекта искать.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/--IE0AMz5f24/TwF12Zg58MI/AAAAAAAAAKQ/JfTE_ICd26U/s1600/03.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="192" src="http://1.bp.blogspot.com/--IE0AMz5f24/TwF12Zg58MI/AAAAAAAAAKQ/JfTE_ICd26U/s640/03.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Затем предложит ввести искомый текст.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-qouKEACw4AM/TwF19szHHNI/AAAAAAAAAKc/cSsge6OHLls/s1600/04.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="192" src="http://4.bp.blogspot.com/-qouKEACw4AM/TwF19szHHNI/AAAAAAAAAKc/cSsge6OHLls/s640/04.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;После этого откроет новое окно с результатами поиска.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-8cEUoO7Nrdg/TwF2F4YblmI/AAAAAAAAAKo/XfjHoMUXY90/s1600/05.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="192" src="http://4.bp.blogspot.com/-8cEUoO7Nrdg/TwF2F4YblmI/AAAAAAAAAKo/XfjHoMUXY90/s640/05.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Результаты поиска -- список файлов. Он кликабельный; при нажатии на любую строку мышкой или клавишей Return, файл откроется в этом же окне.&lt;br /&gt;&lt;br /&gt;Возможно, мой плагин кому-то покажется полезным. Если у кого-нибудь будет желание -- пишите, попробуем вместе допилить его до production-уровня.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-7607463103719360442?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/7607463103719360442/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2012/01/my-projectel.html#comment-form' title='Комментарии: 7'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/7607463103719360442'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/7607463103719360442'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2012/01/my-projectel.html' title='my-project.el'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-owazSt0_OSo/TwFzQk_vhHI/AAAAAAAAAJg/iJqepjBL9h4/s72-c/00.png' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-6089380750768190599</id><published>2011-12-18T18:51:00.000+03:00</published><updated>2011-12-18T18:51:48.626+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><title type='text'>Emacs для разработки на Java</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Меня, как и многих других Java-программистов, сильно не устраивает наша основная IDE: Eclipse. Несмотря на то, что это хорошая IDE, в ней есть серьезный недостаток: крайне убогий текстовый редактор. Он работает по тем же принципам, по которым работают все текстовые редакторы во всех крупных IDE уже лет пятнадцать. Еще во время учебы в школе, когда я программировал в свое удовольствие на Delphi 3.0, уже тогда у меня была IDE по своему функционалу ничуть не уступавшая сегодняшнему Eclipse. И, откровенно говоря, это здорово раздражает: языки развиваются, а инструменты редактирования -- нет.&lt;br /&gt;Сегодня мне от IDE нужно очень многое: и контекстно-зависимый рефакторинг, и автоматическая генерация кода, и умное дополнение. Но самое главное, чего мне не хватает: полная автоматизация всей рутины. Java -- язык крайне многословный. Чтобы реализовать самую небольшую часть функционала зачастую приходится писать массу повторяющегося кода. Даже несмотря на ООП, т.к. не все удается решить наследованием и полиморфизмом в том виде, в котором эти механизмы реализованы в Java.&lt;br /&gt;Уже несколько месяцев я пользуюсь Emacs для написания Java-кода. Это просто потрясающий текстовый редактор! Его так легко расширять новым функционалом, что я делаю это не задумываясь!&lt;br /&gt;Сегодня у меня автоматизированы все повторяющиеся операции. Например, чтобы добавить в код всего одну локализованную строку, необходимо отредактировать минимум три файла: 1) файл Messages.properties, где ключу нужно сопоставить текст; 2) файл Messages.java, где нужно объявить функцию, достающую текст по ключу из файла properties; 3) в файле с описанием UI нужно вызвать этот созданный метод из класса Messages. Как же меня раздражало раньше, когда я делал это вручную! Описываешь формочку из десяти элементов, каждому нужно задать title. И, вместо того, чтобы задумываться над задачей, приходится отвлекаться на поиск этих долбаных Messages.properties и Messages.java и писать туда код богомерзким копи-пейстом! Сейчас мне для этого досточно одного клавиатурного сочетания, и Emacs всё сделает за меня сам. Может быть, я стал слишком ленивым? Знаете, все-таки, это неповторимое ощущение, когда кто-то пишет твой код за тебя!&lt;br /&gt;И так во всём. В каждом проекте есть свои повторяющиеся заморочки. Писать для этого плагин Eclipse -- слишком уж сложно. Вы пишете свои плагины Eclipse для каждого проекта? И я -- нет. Я пишу плагины Emacs.&lt;br /&gt;У Emacs, конечно, есть свои проблемы. Например, достаточно больная тема -- это именно разработка на Java. Все существующие решения для Emacs или безнадежно устарели давно (JDEE), или устарели и больше не поддерживаются совсем недавно (malabar-mode). Емаксеры обычно пишут на C/C++, Java у них не в почете. Но есть один плагин, который закрывает практически все мои потребности в IDE. Это &lt;a href="https://github.com/senny/emacs-eclim"&gt;emacs-eclim&lt;/a&gt;. &lt;br /&gt;На самом деле, этот плагин -- надстройка над другим замечательным проектом: &lt;a href="http://eclim.org/"&gt;eclim&lt;/a&gt;, который умеет интегрироваться с Eclipse. Разработал Eclim в 2005-м году американский программист Эрик Ван Девостин (&lt;a href="https://github.com/ervandew"&gt;Eric Van Dewoestine&lt;/a&gt;) для своих нужд. Он хотел сделать интеграцию Eclipse и GVim. Затем опубликовал исходник, и другой программист, Ивес Сенн (&lt;a href="https://github.com/senny"&gt;Yves Senn&lt;/a&gt;), разработал аналогичный плагин к Emacs.&lt;br /&gt;Emacs-eclim умеет практически всё то же самое, что и Eclipse: контекстно-зависимое дополнение кода, рефакторинг (переименование классов, пакетов и т.д.), подсветка ошибок после компиляции, автоматизированый импорт классов, автоматическая реализация интерфейсов и т.д. На изображении показано, как выглядит моё рабочее окружение Emacs.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-ZFDfUTyjjps/Tu33JDZdKlI/AAAAAAAAAJI/kg0wm-4tnLU/s1600/eclim.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="462" src="http://1.bp.blogspot.com/-ZFDfUTyjjps/Tu33JDZdKlI/AAAAAAAAAJI/kg0wm-4tnLU/s640/eclim.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;Мне удалось побеседовать с Эриком об Eclim. Ниже -- перевод моего небольшого интервью, оригинал которого -- в конце статьи.&lt;br /&gt;&lt;br /&gt;Я: Привет, Эрик! Расскажи немного о себе. Сколько тебе лет, чем занимаешь, где живешь?&lt;br /&gt;Эрик: Мне 33 года, я работаю программным инженером, живу в Лос Анжелесе, занимаюсь в основном веб-разработкой. Еще со школы я пользовался Java, типичным JEE-стеком и т.д. Но примерно лет пять назад я переключился на python, в основном на Python/Django. Вообще, я довольно открыт к различным языкам программирования, хотя и не пробовал ruby до сих пор.&lt;br /&gt;Я: Ага, понял. Так что же тебя заставило разработать eclim? Если программируешь на Java, то у тебя полно чудесных IDE.&lt;br /&gt;Эрик: В школе (&lt;i&gt;судя по всему, имеется в виду "высшая школа". -- примечание автора&lt;/i&gt;), когда я проходил курс по структурам данных с применением С++, все наши лабораторные работы должны были компилироваться и запускаться на Solaris. Поэтому я пользовался vi для всего моего кода. После того, как я достаточно "вьехал" в vi, заметил, что стал гораздо более продуктивным.&lt;br /&gt;Я: Но Java... Для неё же в самом деле нужны IDE для управления проектами, контекстно-зависимого автодополнения и рефакторинга...&lt;br /&gt;Эрик: Потом я перешел на Java и пользовался Borland-ом какое-то время еще в школе, затем попробовал Forte (позже переименованный в NetBeans), т.к. мне нужна была бесплатная IDE. Еще через несколько лет я открыл для себя GVim (vi + подсветка синтаксиса и прочие "плюшки") и сразу же стал им пользоваться. Как ты заметил, GVim-у не хватает многих возможностей IDE.&lt;br /&gt;Я: Так что ж ты не воспользовался Eclipse или NetBeans?&lt;br /&gt;Эрик: Потому что у них слишком сложный интерфейс текстового редактора по сравнению с GVim. А я тратил намного больше времени на набор текста, чем на использование инструментов IDE. &lt;br /&gt;Я: Какие особенности IDE тебя раздражали больше всего?&lt;br /&gt;Эрик: У них нету режимов (&lt;i&gt;имеются в виду режимы редактора vim: режим вставки, навигации, визуальный режим и т.д. -- примечание автора&lt;/i&gt;). Сегодня почти у всех программ есть специальные режимы (vim, vimperator, irsii, readline + много инструментов командной строки). Я обнаружил, что когда убираю пальцы с home row (&lt;i&gt;строка на клавиатуре, где находятся клавиши asdfghjkl. -- примечание автора&lt;/i&gt;) сразу же падает моя продуктивность.&lt;br /&gt;Я: Ага, ОК. Значит, были две основные причины, из-за которых те не пользовался IDE:&lt;br /&gt;1) IDE не позволяет управлять курсором из home row;&lt;br /&gt;2) отсутствие режимов, как в vim.&lt;br /&gt;Кстати, примерно поэтому же и я не пользуюсь IDE. Есть еще одна причина для меня: для IDE очень сложно писать свои плагины :-)&lt;br /&gt;Эрик: Да, это основные причины, по которым я предпочитаю vim. Действительно, входной порог в написание плагинов для IDE слишком высокий, особенно для Java IDE.&lt;br /&gt;Я: Мои друзья, использующие GVim или Emacs, обычно также используют и IDE. Что думаешь про такой способ?&lt;br /&gt;Эрик: Я предпочитаю использовать один интерфейс для разработки. Мне уже приходится переключаться между командной строкой, редактором и броузером (иногда и другими инструментами). Так что добавить еще и IDE -- это не самый лучший выбор.&lt;br /&gt;Я: ОК. Тебе помогали в разработке Eclim? Как отреагировало коммьюнити на твой проект?&lt;br /&gt;Эрик: Это был сольный проект. Я начал его как proof of concept и продолжил наращивать функционал. У меня было пару коллег, которые в самом начале пробовали Eclim, вот, пожалуй, и всё. Что до реакции коммьюнити... Я никогда не анонсировал Eclim и не пиарил его. Я обнаружил, что те, кому была нужна функциональность вроде Eclim, так или иначе найдут его. С годами (а я начал Eclim в 2005-м) я видел весь спектр мнений по поводу Eclim. Я никогда не ожидал, что он подойдет каждому, так что несколько негативных комментариев не сильно меня удивили. В конце концов, я написал Eclim для себя, а то, что он пригодился другим людям, -- просто дополнительный бонус.&lt;br /&gt;Я: А ты всё еще пользуешься Eclim-ом? Ну просто если ты кодишь на python, то зачем тебе Eclim и Java?&lt;br /&gt;Эрик: Я добавил в Eclim поддержку разных языков, включая python, так что я использую Eclim каждый день. И, конечно же, я прдолжаю разрабатывать Eclim :-)&lt;br /&gt;Я: О, это отличная новость! Потому что я нахожу возможности Eclim фантастическими: контекстно-зависимое дополнение кода, подсветка ошибок компиляции и т.д.&lt;br /&gt;Эрик: Всегда приятно слышать, что Eclim нравится людям! :-)&lt;br /&gt;Я: Ну а каких возможностей в Eclim не хватает? Что бы ты хотел туда добавить?&lt;br /&gt;Эрик: У меня огромный todo list, в котором пунктов намного больше, чем я когда-либо смогу выполнить. Я получаю от пользователей  разные запросы и стараюсь приоритезировать их в зависимости от того, насколько запрошенная функциональнасть нужна мне или другим людям. &lt;br /&gt;Я: Можшь назвать три главных по приоритету пункта?&lt;br /&gt;Эрик: Исправление ошибок почти всегда выходит на первое место, так что главный приоритет теперь -- несколько ошибок из создания проектов в CDT (C/C++). Второй приоритет -- несколько фич (сигнатуры методов и классов + документация для запрошенного элемента (как во всплывающей подсказке Eclipse), перенос поддержки python из rope (библиотека для python) в pydev. Остальное -- это множество мелких исправлений и дополнений. Думаю, нужно еще упомянуть то, что я заканчиваю мои scala- и android- ветки.&lt;br /&gt;Я: что было самым сложным для тебя в Eclim? Тебе кто-нибудь помогал впоследствии или только слвлаи запросы на функциональность?&lt;br /&gt;Эрик: Самое сложное для меня -- это Eclipse API (там полно недокументированной функциональности) и поиск способов хоть как-то заставить этот API работать.&lt;br /&gt;Я: Да, там отстойный API. Я тоже пробовал в нем разобраться, но быстро оставил эту затею. Так что насчет коммьюнити? Тебе кто-нибудь помогает сейчас?&lt;br /&gt;Эрик: Eclim хотсится на github, но до сих пор никто не выступил как крупный контрибютер. Так что сейчас я до сих пор единственный разработчик с правами коммита. У меня было несколько pull request-ов или патчей, но они довольно мелкие. Думаю, самое сложное для других программистов -- это Eclipse API. Сообщения об ошибках и активность в e-mail рассылке, также, как и обсуждения в IRC носит эпизодический характер.&lt;br /&gt;Я: ОК, понял. Я уже почти закончил свой список вопросов, остался последний.&lt;br /&gt;Эрик:...так что, конечно, есть коммьюнити пользователей Eclim, но разрабатываю Eclim в основном я сам.&lt;br /&gt;Давай, жги :-)&lt;br /&gt;Я: Единственная причина, по которой я перешел с Vim на Emacs, был VimScript. Emacs Lisp показался мне гораздо мощнее. Что скажешь об этом?&lt;br /&gt;Эрик: Я никогда не пользовался elisp-ом, так что не могу ничего сказать о нем. Думаю, VimScript с версии 7.0 (когда в него добавили списки, словари и прочие фишки) вполне годится для использования, хотя он все еще далек от идеала. Думаю, наибольшая проблема в том, что я и другие пытаются писать сложные плагины и встречаются с тем фактом, что vim -- однопоточный, и не может нормально общаться с долго работающими процессами (netbeans интерфейс к vim не решает этой проблемы). Слыхал, что у с этим Emacs дела намного лучше.&lt;br /&gt;Я: ОК, спасибо за твое время и особенно за Eclim! Мне действительно нравится Eclim, и это мой главный инструмент в работе!&lt;br /&gt;Эрик: Спасибо, я очень рад слышать, что Eclim делает жизнь проще.&lt;br /&gt;&lt;br /&gt;Оригинал:&lt;br /&gt;&lt;br /&gt;&amp;lt;dbushenko&amp;gt; could you please tell a bit about you? How old are your, where do you live and what do you do?&lt;br /&gt;&amp;lt;ervandew&amp;gt; I'm 33, a software engineer living and working in Los Angeles working primary on web based platforms. Out of school I was using Java using the typical JEE stack, etc. but about 5 years ago I switched to python, primarily django based.&lt;br /&gt;&amp;nbsp;I'm pretty open language wise, but I haven't dabbled much in ruby yet.&lt;br /&gt;&amp;lt;dbushenko&amp;gt; aha, I see&lt;br /&gt;&amp;nbsp;but what did make you to start eclim?&lt;br /&gt;&amp;nbsp;bcs if you program in java&lt;br /&gt;&amp;nbsp;you have many wonderfull IDEs&lt;br /&gt;&amp;lt;ervandew&amp;gt; While in school I was taking a data structures class using c++ where all our submissions had to compile and run on the a solaris box, so I picked up vi to write all my code.&lt;br /&gt;&amp;nbsp;After getting past the learning curve I quickly found myself more productive.&lt;br /&gt;&amp;lt;dbushenko&amp;gt; but java... it really needs some IDEs for better project management, context-sensitive autocompletion and refactoring... &lt;br /&gt;&amp;lt;ervandew&amp;gt; I later moved to Java and used Borland for a bit while still in school and switch to Forte (later renamed to NetBeans) since I wanted a free IDE.&lt;br /&gt;&amp;lt;ervandew&amp;gt; A few years later I discovered gvim (vi + syntax highlighting and other goodies) and switch almost immediately.&lt;br /&gt;&amp;lt;ervandew&amp;gt; As you mentioned though gvim was missing all the IDE goodies.&lt;br /&gt;&amp;lt;dbushenko&amp;gt; and why didn't you just switch to eclipse or netbeans?&lt;br /&gt;&amp;lt;ervandew&amp;gt; Because their editing interface was some cumbersome when compared to gvim, and I spent way more time typing than using the IDE's additional tools&lt;br /&gt;&amp;nbsp;*so cumbersome&lt;br /&gt;&amp;lt;dbushenko&amp;gt; which features of the IDE's editor did you hate most?&lt;br /&gt;&amp;lt;ervandew&amp;gt; That they are all modeless. Today almost every program I use has modes (vim, vimperator, irssi, readline + many command line tools)&lt;br /&gt;&amp;nbsp;I find taking my finger off the home row results in an immediate drop in productivity.&lt;br /&gt;&amp;lt;dbushenko&amp;gt; aha, ok...&lt;br /&gt;&amp;nbsp;so there were two major reasons:&lt;br /&gt;&amp;nbsp;1) IDEs didn't allow to browse with the home row&lt;br /&gt;&amp;nbsp;2) absence of vim-modes&lt;br /&gt;&amp;nbsp;right?&lt;br /&gt;&amp;nbsp;btw, this is mostly the same reasons for me, but I have one more: IDEs really hard to extend with your own plugins :-)&lt;br /&gt;&amp;lt;ervandew&amp;gt; yeah those are the core reasons I prefer editing via vim&lt;br /&gt;&amp;nbsp;true, extending an IDE typically has a higher barrier to entry, especially java IDEs&lt;br /&gt;&amp;lt;dbushenko&amp;gt; My friends who also use gvim or Emacs usually try to use both: an editor and an IDE. what do you think about that way?&lt;br /&gt;&amp;lt;ervandew&amp;gt; I prefer to use one interface for my coding. I already have to switch between a terminal, editor, and browser (sometimes other things) so adding an IDE to the mix is not ideal for me.&lt;br /&gt;&amp;lt;dbushenko&amp;gt; ok, next:&lt;br /&gt;&amp;nbsp;did anyone help you with eclim when you've started it? and what was the communitiy's response to it?&lt;br /&gt;&amp;lt;ervandew&amp;gt; It was a solo project. It started as a proof of concept and grew from there. I had a couple of coworkers that were early adopters but that was about it.&lt;br /&gt;&amp;nbsp;As far as the communities response...&lt;br /&gt;&amp;nbsp;I never went out and announced eclim or marketed in any way. I figured if people wanted something like it, they would find it and I'd let word get out organically. Over the years (I started writing eclim in 2005) I seen various response all along the spectrum. I never expected eclim to be for everybody so the occasional negative comment doesn't surprise me. Ultimately I wrote eclim for myself, helping other people is just an added bonus.&lt;br /&gt;&amp;lt;dbushenko&amp;gt; and do you still use eclim for your needs? I mean, if you code mostly python, do you still need eclim and java?&lt;br /&gt;&amp;lt;ervandew&amp;gt; I've added support for various languages into eclim including python so I still use it every day and I of course use eclim to continue my work on eclim :)&lt;br /&gt;&amp;lt;dbushenko&amp;gt; oh, this is great news! :-)&lt;br /&gt;&amp;nbsp;bcs I've found that eclim is fantastic:&lt;br /&gt;&amp;nbsp;it has context-sensiteve autocompletion, compilation errors, etc.&lt;br /&gt;&amp;lt;ervandew&amp;gt; It's always good to hear that people find it useful :)&lt;br /&gt;&amp;lt;dbushenko&amp;gt; but what features does it lack? what do you think you would like to add to it soon?&lt;br /&gt;&amp;lt;ervandew&amp;gt; I have a huge todo list with far more on it that I could ever actually get done. I get various requests from users here and there and I try to prioritize features based on whether I'd use it or if other people would find it useful.&lt;br /&gt;&amp;lt;dbushenko&amp;gt; can you mention 3 highest priorites from your todo list?&lt;br /&gt;&amp;lt;ervandew&amp;gt; Bugs almost always come first, so the highest priority are a couple CDT (c/c++) project creation bugs, but features wise: method/class signature + docs for the requested element (like a hover type info), moving python support from rope (python library) to pydev.&amp;nbsp; Those are two big ones. The rest of my high priority stuff is a lot of small things to help solidify eclim and work out kinks on other platforms or configurations.&lt;br /&gt;&amp;nbsp;I suppose I should also mention finishing up my scala and android branches&lt;br /&gt;&amp;lt;dbushenko&amp;gt; what was the most difficult to you while developing eclim? do people commit to the project or just send you bug reports and features request?&lt;br /&gt;&amp;lt;ervandew&amp;gt; the most difficult thing was wrapping my head around the eclipse APIs (they don't document most of the parts eclim has to hook into) and finding ways to get some features to work in a headless environment since a lot of things are closely tied to the UI&lt;br /&gt;&amp;lt;dbushenko&amp;gt; yeah, it's a crappy API! I tried to used and gave it up completely!&lt;br /&gt;&amp;nbsp;so what about community? is it involved into the project now?&lt;br /&gt;&amp;lt;ervandew&amp;gt; Eclim is hosted on github and no one has stepped up as a large contributor so I'm currently the only one with commit rights to the repos. I get the occasional pull request or patch, but they tend to be few and far between. I think the biggest barrier being those eclipse APIs. Issue reports and email activity is bursty, as is activity on the eclim IRC channel.&lt;br /&gt;&amp;lt;dbushenko&amp;gt; ok, I see...&lt;br /&gt;&amp;nbsp;and I've already finished my list of questions. the last one:&lt;br /&gt;&amp;lt;ervandew&amp;gt; So there is definitely a community of users, but on the development end it's primarily just me&lt;br /&gt;&amp;nbsp;fire away&lt;br /&gt;&amp;lt;dbushenko&amp;gt; The only reason to me when I switched from gvim to Emacs was VimScript. Emacs Lisp seemed much more powerfull to me. what is your attitude to Emacs Lisp comparing to VimScript? &lt;br /&gt;&amp;lt;ervandew&amp;gt; I haven't used elisp so I can't comment on that. I think vim script post 7.0 (where lists, dictionaries, and other features were introduced) is livable but far from ideal. I think the biggest issue that myself and others attempting to write complex plugins face is the fact that vim is single threaded and has no good way to interact with long running processes (the netbeans interface in vim doesn't solve that problem).&lt;br /&gt;&amp;nbsp;I've heard that emacs shines in that area&lt;br /&gt;&amp;lt;dbushenko&amp;gt; Ok. Thanks for your time and especially for Eclim! I really love Eclim, it's my major tool for my job!&lt;br /&gt;&amp;lt;ervandew&amp;gt; My pleasure, and again I'm happy to hear eclim has helped make life easier :)&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-6089380750768190599?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/6089380750768190599/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2011/12/emacs-java.html#comment-form' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/6089380750768190599'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/6089380750768190599'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2011/12/emacs-java.html' title='Emacs для разработки на Java'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-ZFDfUTyjjps/Tu33JDZdKlI/AAAAAAAAAJI/kg0wm-4tnLU/s72-c/eclim.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-6617083423553979296</id><published>2011-12-11T15:07:00.001+03:00</published><updated>2011-12-11T21:27:18.357+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><category scheme='http://www.blogger.com/atom/ns#' term='общение'/><title type='text'>Впечатление о встрече разработчиков Scala #7</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Вчера, в субботу, выступал с докладом на 7-й встрече любителей функционального программирования. Тема конференции -- "Инструменты, упрощающие разработку программ".&lt;br /&gt;К организации встречи подошли довольно серьзено. Во-первых, мне удалось уговорить выступить с докладом Алекса Отта. Тема его доклада -- "Emacs -- универсальный комбайн". Было очень интересно послушать про Emacs от настоящего мастера ФП и знатока Emacs.&lt;br /&gt;Во-вторых, мы с Алексом специально для встречи написали небольшое пособие по программированию Emacs. Компания Epam помогла нам его опубликовать и даже предоставила дизайнера, который красиво оформил обложку и текст. Спасибо тебе, Руслан!&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-_9ZAS0Q2KQk/TuSR2P4b5II/AAAAAAAAAI4/6zKeiZwBmZs/s1600/scalaby7_0007.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="265" src="http://3.bp.blogspot.com/-_9ZAS0Q2KQk/TuSR2P4b5II/AAAAAAAAAI4/6zKeiZwBmZs/s400/scalaby7_0007.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;На фотке -- я и Алекс Отт презентуем пособие "Программирование Emacs для редактирования и рефакторинга кода". Пособие, кстати, раздали бесплатно всем желающим. И много еще осталось. У меня на руках еще 4 штуки лишних, могу подарить.&lt;br /&gt;Электронную версию пособия можно взять &lt;a href="https://docs.google.com/open?id=0BzmL7xzGeOtOOWE1ZTc3NzAtZjkyNy00ZDU5LTg2Y2UtZDc4MmNkNTI1ZmJl"&gt;здесь&lt;/a&gt;.&lt;br /&gt;Свой доклад я посвятил программированию Emacs под свои задачи. Моё личное мнение: Emacs надерет задницу всем текстовым редакторам и IDE только если вы сами будете его программировать для себя. В своем докладе я показал, как разрабатывать скрипты на Emacs Lisp и как автоматизировать нудную рутинную работу. Видео моего выступления можно посмотреть &lt;a href="http://video.yandex.by/users/d-bushenko/view/1/"&gt;здесь&lt;/a&gt;.&lt;br /&gt;Вопросов по программированию Emacs было много, так что мне явно удалось заинтересовать ребят этим текстовым редактором. Спасибо Василию Ременюку за организацию встречи Scala #7 и возможность выступить с докладом о Emacs! &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-6617083423553979296?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/6617083423553979296/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2011/12/scala-7.html#comment-form' title='Комментарии: 8'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/6617083423553979296'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/6617083423553979296'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2011/12/scala-7.html' title='Впечатление о встрече разработчиков Scala #7'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-_9ZAS0Q2KQk/TuSR2P4b5II/AAAAAAAAAI4/6zKeiZwBmZs/s72-c/scalaby7_0007.jpg' height='72' width='72'/><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-3598253218133501336</id><published>2011-12-01T22:33:00.000+03:00</published><updated>2011-12-01T22:33:54.372+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='clojure'/><title type='text'>Scala, подвинься!</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-wPw2wNHrbYg/TtfQ88LDEDI/AAAAAAAAAIw/QUApuYT7y-g/s1600/avout-logo.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="68" src="http://3.bp.blogspot.com/-wPw2wNHrbYg/TtfQ88LDEDI/AAAAAAAAAIw/QUApuYT7y-g/s320/avout-logo.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;Буквально на днях зарелизилась офигеннейшая библиотека &lt;a href="http://avout.io/"&gt;Avout&lt;/a&gt;. Авторы библиотеки -- небезызвестная clojure/core. Эта маленькая конторка, основанная Ричем Хикки, занимается исключительно разработкой и поддержкой кложуры. Так что библиотека Avout -- самая что ни на есть true-реализация Distributed Software Transactional Memory!&lt;br /&gt;Вообще-то стоило бы привести здесь примеры исходников... Но именно для avout я этого делать не буду. Дело в том, что он настолько прост в освоении, что диву даёшься! Еще ни разу не встречал RPC или MQ, освоить которые было бы настолько легко. Лично у меня всё завелось и заработало спустя 10 минут после того, как я открыл Emacs. Одним словом, впечатления от avout -- самые положительные!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-3598253218133501336?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/3598253218133501336/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2011/12/scala.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/3598253218133501336'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/3598253218133501336'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2011/12/scala.html' title='Scala, подвинься!'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-wPw2wNHrbYg/TtfQ88LDEDI/AAAAAAAAAIw/QUApuYT7y-g/s72-c/avout-logo.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-5294885824028284050</id><published>2011-11-30T13:35:00.001+03:00</published><updated>2011-11-30T13:36:07.159+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='computer science'/><title type='text'>Типы в языках программирования</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-1k1qEIwXucM/TtYEK25PdjI/AAAAAAAAAIo/gzGBYuANgnc/s1600/Tipi-v-yzikah-programmirovaniy-Bendjamin-Pirs_10220728_ec9f1cb7.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://1.bp.blogspot.com/-1k1qEIwXucM/TtYEK25PdjI/AAAAAAAAAIo/gzGBYuANgnc/s200/Tipi-v-yzikah-programmirovaniy-Bendjamin-Pirs_10220728_ec9f1cb7.jpg" width="149" /&gt;&lt;/a&gt;&lt;/div&gt;Недавно Алекс Отт делал объяву о том, что вышла книга "Типы в языках программирования". Доставить в Минск её было непросто: на ozon.ru она стоит в районе 2000, цены в магазинах -- 1100-1300 (в росс. рублях) плюс доставка -- короче сложно, долго, дорого. Я сделал запрос на oz.by, и через два дня они дали ответ: книгу привезут в Минск! Заказать её можно &lt;a href="http://oz.by/books/more10220728.html"&gt;здесь&lt;/a&gt; за 482574 бел.руб., что в долларах составляет примерно 56$ или примерно 1756 росс. руб.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-5294885824028284050?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/5294885824028284050/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2011/11/blog-post.html#comment-form' title='Комментарии: 4'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/5294885824028284050'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/5294885824028284050'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2011/11/blog-post.html' title='Типы в языках программирования'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-1k1qEIwXucM/TtYEK25PdjI/AAAAAAAAAIo/gzGBYuANgnc/s72-c/Tipi-v-yzikah-programmirovaniy-Bendjamin-Pirs_10220728_ec9f1cb7.jpg' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-175811671202660236</id><published>2011-11-22T12:39:00.001+03:00</published><updated>2011-11-24T14:41:03.846+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><title type='text'>Scala.by #7 (updated)</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-4TJ7podhdec/Tsttvx2dDdI/AAAAAAAAAIg/yE3zfUPpE6s/s1600/%25D0%25BE%25D0%25B1%25D0%25BB%25D0%25BE%25D0%25B6%25D0%25BA%25D0%25B0.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://3.bp.blogspot.com/-4TJ7podhdec/Tsttvx2dDdI/AAAAAAAAAIg/yE3zfUPpE6s/s200/%25D0%25BE%25D0%25B1%25D0%25BB%25D0%25BE%25D0%25B6%25D0%25BA%25D0%25B0.png" width="140" /&gt;&lt;/a&gt;&lt;/div&gt;10-го декабря выступлю с докладом на встрече любителей функциональщины (scala.by). Рассказывать буду о том, как запрограммировать Emacs под себя. Главная тема доклада: автоматизация кастомного рефакторинга.&lt;br /&gt;Участников встречи ждет небольшой подарок: пособие по программированию Emacs, которое мы вместе с Алексом Оттом готовим специально для встречи.&lt;br /&gt;&lt;br /&gt;Текст пособия уже практически готов и завтра отправится к дизайнерам на оформление.&lt;br /&gt;&lt;br /&gt;Предварительная версия содержания:&lt;br /&gt;&lt;br /&gt;1. &lt;i&gt;&lt;u&gt;Введение&lt;/u&gt; &lt;/i&gt;(зачем программировать Emacs)&lt;i&gt;.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;2. &lt;u&gt;&lt;i&gt;Коротко о GNU/Emacs&lt;/i&gt;&lt;/u&gt; (где хранятся файлы настроек, как изменить значения глобальных переменных, как выполнять команды, как получить справку по функциям и переменным, что такое буфер, какие режимы бывают и т.д.)&lt;br /&gt;&lt;br /&gt;3. &lt;u&gt;&lt;i&gt;Основы Emacs Lisp&lt;/i&gt;&lt;/u&gt; (что такое префиксная нотация, формы, как писать программы на Emacs Lisp, как их компилировать и выполнять, какие бывают типы данных, что такое символы и как они отличаются от переменных, как объявлять функции и какие бывают управляющие конструкции; манипуляция списками; ввод-вывод)&lt;br /&gt;&lt;br /&gt;4. &lt;i&gt;&lt;u&gt;Примеры elisp-скриптов&lt;/u&gt; &lt;/i&gt;(автоматизированный импорт Java-класса в программе на Clojure; генерация геттеров/сеттеров в Java-классе; автоматическая генерация и инициализация полей Java-класса в Android-приложении; преобразование html-файла в haml-формат; автоматическое создание конструктора для неизменяемых Java-объектов)&lt;br /&gt;&lt;br /&gt;5. &lt;u&gt;&lt;i&gt;Справочник часто употребимых функций&lt;/i&gt;&lt;/u&gt; (перемещение курсора, получение позиции, управление регионом, поиск текста, работа с буфером, ввод данных с подсказкой)&lt;br /&gt;&lt;br /&gt;6. &lt;u&gt;&lt;i&gt;Пакеты для разработчика&lt;/i&gt;&lt;/u&gt; (краткое перечисление с описанием наиболее полезных расширений Emacs)&lt;br /&gt;&lt;br /&gt;7. &lt;i&gt;&lt;u&gt;Литература&lt;/u&gt; &lt;/i&gt;(а также -- полезные&lt;br /&gt;ссылки)&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-175811671202660236?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/175811671202660236/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2011/11/scalaby-7.html#comment-form' title='Комментарии: 5'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/175811671202660236'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/175811671202660236'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2011/11/scalaby-7.html' title='Scala.by #7 (updated)'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-4TJ7podhdec/Tsttvx2dDdI/AAAAAAAAAIg/yE3zfUPpE6s/s72-c/%25D0%25BE%25D0%25B1%25D0%25BB%25D0%25BE%25D0%25B6%25D0%25BA%25D0%25B0.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-6462924426740314559</id><published>2011-10-20T16:40:00.004+03:00</published><updated>2011-10-20T16:59:02.340+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Генератор кода</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;style type="text/css"&gt; &lt;!--   @page { size: 21cm 29.7cm; margin: 2cm }   P { margin-bottom: 0.21cm }  --&gt;  &lt;/style&gt;  &lt;br /&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;Работая java-программистом остро ощущаю нехватку средств метапрограммирования. Макросов не хватает катастрофически, а средства интроспекции не позволяют добиться той же гибкости и производительности. На днях, разрабатывая очередную формочку для android-приложения, окончательно ощутил себя обезьяной. Не выдержал и сделал маленький генератор кода для быстрого построения простых DSL-ов.&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;Пример приведу из Java/Swing. Чтобы реализовать MVC для нужно иметь три класса (и, соответственно, три файла для них); класс-контроллер должен их всех проинициализировать и подписать View на события от Model. В Model нужно держать набор данных и аксессоров к ним. В View, помимо самих компонентов, нужно иметь еще и метод, обновляющий их содержимое, когда изменяется модель. Вопрос: что из перечисленного действительно должен делать программист? Если формочка одна – то можно все и вручную сделать, но если формочек 10-20, то конкретно задалбывает писать все это вручную. Поэтому я сделал вот такой &lt;a href="https://github.com/dbushenko/clj-gen"&gt;генератор кода&lt;/a&gt;.&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;Вот, что нам известно по ТЗ об очередной формочке, для которой делаем MVC. Форма состоит из двух полей (Text и Number), одноименные данные содержатся в модели. Их же из модели и нужно вытягивать, если она изменяется.&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;Генерим код следующим образом (файл scripts/mvc.clj):&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;(def *controller* "MyController")&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;(def *view* "MyView")&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;(def *model* "MyModel")&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;(def *view-components* [{:name "Text", :type "JTextField", :sync true}&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;{:name "Number", :type "JTextField", :sync true}])&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;(def *model-data* [{:name "Text", :type "String"}&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;{:name "Number", :type "int"}])&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;(defn process-view []&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;(eval-template "templates/View.soy"&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;{:vname *view*&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;:cname *controller*&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;:mname *model*&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;:components *view-components*}))&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;(defn process-controller []&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;(eval-template "templates/Controller.soy"&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;{:vname *view*&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;:cname *controller*&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;:mname *model*}))&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;(defn process-model []&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;(eval-template "templates/Model.soy"&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;{:name "MyModel"&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;:data *model-data*}))&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;(write-text (process-view) "MyView.java")&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;(write-text (process-controller) "MyController.java")&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;(write-text (process-model) "MyModel.java")&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;Шаблоны приводить не буду, они есть в проекте генератора. Сгенерированный файл MyView.clj:&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;package swingtest;  &lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;import java.awt.event.ActionEvent;  &lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;import java.awt.event.ActionListener;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;import java.util.Observable;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;import java.util.Observer;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;import javax.swing.Box;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;import javax.swing.JButton;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;import javax.swing.JFrame;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;import javax.swing.JTextField;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;public class MyView extends JFrame implements Observer{&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;private MyController _Controller;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;private JTextField _Text;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;private JTextField _Number;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;MyView(MyController controller) {&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;_Controller = controller;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;initLayout();&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;}&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;protected void initLayout() {&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;setBounds(100, 100, 200, 100);&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;Box box = Box.createVerticalBox();&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;// Initialize your class here...&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;//&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;}&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;@Override  &lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;public void update(Observable o, Object arg) {&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;MyModel model = (MyModel)o;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;_Text.setText(String.valueOf(model.getText()));&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;_Number.setText(String.valueOf(model.getNumber()));&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;}&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;}&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;Как видно из кода, сгенерен класс MyView, который содержит указанные поля и даже умеет автоматически обновлять их значения, когда меняется модель.&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;Класс модели:&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;import java.util.Observable;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;public class MyModel extends Observable {&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;public void updateModel() {&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;setChanged();&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;notifyObservers();&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;clearChanged();&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;}&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;/* Accessors */&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;private String _Text;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;private int _Number;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;public String getText(){&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;return Text;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;}&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;public void setText(String value){&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;_Text = value;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;}&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;public int getNumber(){&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;return Number;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;}&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;public void setNumber(int value){&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;_Number = value;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;}&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;}&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="margin-bottom: 0cm;"&gt;Модель не только содержит указанные данные, но также и готовые аксессоры к ним.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-6462924426740314559?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/6462924426740314559/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2011/10/blog-post_20.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/6462924426740314559'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/6462924426740314559'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2011/10/blog-post_20.html' title='Генератор кода'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-3375981007534978867</id><published>2011-10-11T09:07:00.005+03:00</published><updated>2011-10-11T10:38:44.400+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='storm'/><title type='text'>Clojure Killer App (updated)</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Для каждого языка программирования обязательно нужна хотя бы одна технология, которая сделает его (язык) популярным. Для Java -- это J2EE, для Ruby -- Ruby on Rails, для Python -- Django. Все это так или иначе связано с вебом. Поэтому, когда появился Clojure, многие ждали, что появится и качественный фреймворк для разработки сайтов на Clojure. Однако, сегодняшний уровень развития web-технологий для Clojure пока что не производит впечатления. Может быть потому, что Clojure предназначен быть номером один в несколько других областях?&lt;br /&gt;19 сентября &lt;a href="http://groups.google.com/group/clojure/msg/dea0fc21ea92bf6b"&gt;Nathanmarz&lt;/a&gt; открыл исходники проекта &lt;a href="https://github.com/nathanmarz/storm"&gt;Storm&lt;/a&gt;, проекта, используемого в Twitter для анализа потоков твитов. Storm -- детище Backtype, недавно купленной Twitter-ом. У Backtype есть еще несколько открытых проектов, среди которых -- &lt;a href="https://github.com/nathanmarz/cascalog/"&gt;Cacalog&lt;/a&gt; (Clojure-обертка над Hadoop) и &lt;a href="https://github.com/nathanmarz/elephantdb"&gt;ElephantDB&lt;/a&gt;.&lt;br /&gt;И Hadoop, и Storm -- технологии, позволяющие легко параллелить вычисления на кластере. Главное отличие между ними в том, что Hadoop выполняет конечные map/reduce задания, в то время, как Storm способен обрабатывать бесконечные потоки данных. Hadoop -- это технология пакетной обработки данных, а Storm -- обработки в real-time.&lt;br /&gt;Storm почти целиком написан на Clojure. 98% функционала Storm -- это Clojure. Правда, если посчитать процент кода на Clojure и процент кода на других языках -- то здесь Clojure займет около 50%. Остальной код -- это привязки к прочим языка программирования, таким как Java и Python.&lt;br /&gt;У Storm-а несколько своеобразная терминология. Минимальными структурными единицами проекта на Storm являются источники (spout) и фильтры (bolts). Термин bolt сложно перевести на русский язык более-менее правильно, но его суть в том, что bolt преобразовывает поток данных из источника в другой поток данных. Так что воспользуемся значением глагола to bolt -- просеивать через сито. Хотя как знать, может Nathanmarz имел в виду именно фонтан (spout) и молния (bolt) -- это я пока что не успел выяснить.&lt;br /&gt;Источники и фильтры объединяются в топологию, про которую подробнее стоит почитать &lt;a href="https://github.com/nathanmarz/storm/wiki/Clojure-DSL"&gt;здесь&lt;/a&gt;.&lt;br /&gt;Ниже -- пример очень простого приложения на Storm.&lt;br /&gt;&lt;div style="overflow: auto;"&gt;&lt;pre style="background: #ffffff; color: black;"&gt;&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;ns&lt;/span&gt; storm-test&lt;span style="color: #808030;"&gt;.&lt;/span&gt;core&lt;br /&gt;  &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;:import&lt;/span&gt; &lt;span style="color: #808030;"&gt;[&lt;/span&gt;backtype&lt;span style="color: #808030;"&gt;.&lt;/span&gt;storm StormSubmitter LocalCluster&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;:use&lt;/span&gt; &lt;span style="color: #808030;"&gt;[&lt;/span&gt;backtype&lt;span style="color: #808030;"&gt;.&lt;/span&gt;storm clojure config&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;:gen-class&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;defspout&lt;/span&gt; number-spout &lt;span style="color: #808030;"&gt;[&lt;/span&gt;&lt;span style="color: #0000e6;"&gt;"number"&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #808030;"&gt;[&lt;/span&gt;conf context collector&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;spout&lt;/span&gt;&lt;br /&gt;     &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;nextTuple&lt;/span&gt; &lt;span style="color: #808030;"&gt;[&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;br /&gt;                &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;Thread/sleep&lt;/span&gt; &lt;span style="color: #008c00;"&gt;100&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;                &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;emit-spout!&lt;/span&gt; collector &lt;span style="color: #808030;"&gt;[&lt;/span&gt;&lt;span style="color: #008c00;"&gt;17&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;     &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;ack&lt;/span&gt; &lt;span style="color: #808030;"&gt;[&lt;/span&gt;id&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;defbolt&lt;/span&gt; neg-bolt &lt;span style="color: #808030;"&gt;[&lt;/span&gt;&lt;span style="color: #0000e6;"&gt;"neg"&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt; &lt;span style="color: #808030;"&gt;[&lt;/span&gt;tuple collector&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;let&lt;/span&gt; &lt;span style="color: #808030;"&gt;[&lt;/span&gt;result &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;-&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;.getInteger&lt;/span&gt; tuple &lt;span style="color: #008c00;"&gt;0&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;emit-bolt!&lt;/span&gt; collector &lt;span style="color: #808030;"&gt;[&lt;/span&gt;result&lt;span style="color: #808030;"&gt;]&lt;/span&gt; &lt;span style="color: #400000;"&gt;:anchor&lt;/span&gt; tuple &lt;span style="color: #400000;"&gt;:p&lt;/span&gt; &lt;span style="color: #008c00;"&gt;2&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;ack!&lt;/span&gt; collector tuple&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;defn&lt;/span&gt; mk-topology &lt;span style="color: #808030;"&gt;[&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;topology&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #808030;"&gt;{&lt;/span&gt;&lt;span style="color: #008c00;"&gt;1&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;spout-spec&lt;/span&gt; number-spout&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;}&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #808030;"&gt;{&lt;/span&gt;&lt;span style="color: #008c00;"&gt;2&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;bolt-spec&lt;/span&gt; &lt;span style="color: #808030;"&gt;{&lt;/span&gt;&lt;span style="color: #008c00;"&gt;1&lt;/span&gt; &lt;span style="color: #400000;"&gt;:shuffle&lt;/span&gt;&lt;span style="color: #808030;"&gt;}&lt;/span&gt; neg-bolt&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;}&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;defn&lt;/span&gt; run-local&lt;span style="color: #808030;"&gt;!&lt;/span&gt; &lt;span style="color: #808030;"&gt;[&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;let&lt;/span&gt; &lt;span style="color: #808030;"&gt;[&lt;/span&gt;cluster &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;LocalCluster.&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;.submitTopology&lt;/span&gt; cluster &lt;span style="color: #0000e6;"&gt;"test"&lt;/span&gt; &lt;span style="color: #808030;"&gt;{&lt;/span&gt;TOPOLOGY-DEBUG true&lt;span style="color: #808030;"&gt;}&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;mk-topology&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;Thread/sleep&lt;/span&gt; &lt;span style="color: #008c00;"&gt;1000&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;.shutdown&lt;/span&gt; cluster&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;Функция run-local! создает фиктивный кластер на одном компьютере, позволяющий просто запустить программу локально. На этот кластер подается топология, созданная функцией mk-topology.&lt;br /&gt;В функции mk-topology вызывается topology с двумя параметрами: словарем источников и словарем фильтров.&lt;br /&gt;Запись &lt;span style="color: #808030;"&gt;{&lt;/span&gt;&lt;span style="color: #008c00;"&gt;1&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;spout-spec&lt;/span&gt; number-spout&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;}&lt;/span&gt; означет, что создается spout с айдишником 1 при помощи функции number-spout.&lt;br /&gt;Аналогично, запись&lt;span style="color: #808030;"&gt;{&lt;/span&gt;&lt;span style="color: #008c00;"&gt;2&lt;/span&gt; &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;bolt-spec&lt;/span&gt; &lt;span style="color: #808030;"&gt;{&lt;/span&gt;&lt;span style="color: #008c00;"&gt;1&lt;/span&gt; &lt;span style="color: #400000;"&gt;:shuffle&lt;/span&gt;&lt;span style="color: #808030;"&gt;}&lt;/span&gt; neg-bolt&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;} &lt;/span&gt;&lt;span style="color: #808030;"&gt;&lt;/span&gt;означает, что создается bolt с айдишником 2 при помощи функции neg-bolt, будет получать данные из источника 1.&lt;br /&gt;Функция number-spout создает spout, который 10 раз в секунду способен выдать вектор с одним единственным данным. Предполагается, что данные spout должен получать из внешних источников, например, очередей сообщений, таких как &lt;a href="https://github.com/nathanmarz/storm-kestrel"&gt;Kestrel&lt;/a&gt;. Здесь же для наглядности я просто передаю число 17.&lt;br /&gt;Функция neg-bolt получает данные из number-spout и отдает их же, но со знаком "минус". Параметр &lt;a href="http://nathanmarz.github.com/storm/doc/backtype/storm/tuple/Tuple.html"&gt;tuple&lt;/a&gt; -- это объект класса Tupple, умеющего извлекать типизированные данные из внутреннего массива.&lt;br /&gt;Файл проекта project.clj выглядит так:&lt;br /&gt;&lt;pre style="background: #ffffff; color: black;"&gt;&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;defproject&lt;/span&gt; storm-test &lt;span style="color: #0000e6;"&gt;"1.0.0-SNAPSHOT"&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #400000;"&gt;:description&lt;/span&gt; &lt;span style="color: #0000e6;"&gt;"My first Storm project"&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #400000;"&gt;:aot&lt;/span&gt; &lt;span style="color: #400000;"&gt;:all&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #400000;"&gt;:dependencies&lt;/span&gt; &lt;span style="color: #808030;"&gt;[&lt;/span&gt;&lt;span style="color: #808030;"&gt;[&lt;/span&gt;org&lt;span style="color: #808030;"&gt;.&lt;/span&gt;clojure&lt;span style="color: #808030;"&gt;/&lt;/span&gt;clojure &lt;span style="color: #0000e6;"&gt;"1.2.1"&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;br /&gt;                 &lt;span style="color: #808030;"&gt;[&lt;/span&gt;org&lt;span style="color: #808030;"&gt;.&lt;/span&gt;clojure&lt;span style="color: #808030;"&gt;/&lt;/span&gt;clojure-contrib &lt;span style="color: #0000e6;"&gt;"1.2.0"&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #400000;"&gt;:dev-dependencies&lt;/span&gt; &lt;span style="color: #808030;"&gt;[&lt;/span&gt;&lt;span style="color: #808030;"&gt;[&lt;/span&gt;storm &lt;span style="color: #0000e6;"&gt;"0.5.3"&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;/pre&gt;Запускать проект нужно через lein repl следующим образом:&lt;br /&gt;&lt;br /&gt;&amp;gt; (use 'storm-test.core)&lt;br /&gt;&amp;gt; (run-local!)&lt;br /&gt;&lt;br /&gt;Короче, думаю, час Clojure настал.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Updation&lt;/b&gt;: Выяснил у Nathanmarz-а насчет терминов. Вот что он пишет:&lt;br /&gt;&amp;nbsp;&lt;i&gt;"Spout" is meant to mean "source of streams", like how a water spout &lt;/i&gt;&lt;br /&gt;&lt;i&gt;is a source of a stream of water.&lt;/i&gt;&lt;br /&gt;&lt;i&gt;"Bolt" is meant to refer to lightning, as in a "storm" is composed of &lt;/i&gt;&lt;br /&gt;&lt;i&gt;"lightning bolts".&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Таким образом, думаю, стоит переводить термины следующим образом:&lt;br /&gt;&lt;u&gt; spout&lt;/u&gt; -- источник&lt;br /&gt;&lt;u&gt;bolt&lt;/u&gt; -- молния.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-3375981007534978867?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/3375981007534978867/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2011/10/clojure-killer-app.html#comment-form' title='Комментарии: 8'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/3375981007534978867'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/3375981007534978867'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2011/10/clojure-killer-app.html' title='Clojure Killer App (updated)'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-423301394214327925</id><published>2011-10-05T12:33:00.000+03:00</published><updated>2011-10-05T12:33:02.173+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>Древний артефакт</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;a href="http://3.bp.blogspot.com/-th_7siPxHM8/Towg0j_i-LI/AAAAAAAAAIA/q9WBkQO1BOE/s1600/indinana.jpg" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="320" src="http://3.bp.blogspot.com/-th_7siPxHM8/Towg0j_i-LI/AAAAAAAAAIA/q9WBkQO1BOE/s320/indinana.jpg" width="227" /&gt;&lt;/a&gt;На днях мне попался в руки древний &lt;a href="https://github.com/xach/linj"&gt;артефакт&lt;/a&gt;.  Это&amp;nbsp; потрясающая вещь неизвестного (почти) происхождения и предназначения. Я натурально чувствовал себя археологом, когда запускал эту штуку. Думаю, Шлиман испытывал похожие ощущения, когда раскапывал Трою...&lt;br /&gt;Методом проб и ошибок удалось выяснить про артефакт следующее:&lt;br /&gt;1. Это имплементация Common Lisp'а, не исключающая своего внеземного происхождения.&lt;br /&gt;2. Работает он исключительно на платформе другого лиспа, что объясняет странные изображения Уробороса на артефакте.&lt;br /&gt;3. Результатом выполненения lisp-программы является java-код, вполне осмысленный и читабельный.&lt;br /&gt;4. Расшифровка сопровождающих артефакт манускриптов показала наличие прозрачной интеграции с существующим java-кодом.&lt;br /&gt;&lt;br /&gt;Из личных впечатлений. Если артефакт покажет свою стабильность, то, вполне возможно, это тот самый Святой Грааль, который я уже давно ищу. Ведь работать хочется на лиспе, а приходится на java...&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-423301394214327925?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/423301394214327925/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2011/10/blog-post_05.html#comment-form' title='Комментарии: 3'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/423301394214327925'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/423301394214327925'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2011/10/blog-post_05.html' title='Древний артефакт'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-th_7siPxHM8/Towg0j_i-LI/AAAAAAAAAIA/q9WBkQO1BOE/s72-c/indinana.jpg' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-5119226318289861988</id><published>2011-10-03T13:22:00.003+03:00</published><updated>2011-10-03T13:36:15.802+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>Элитные языки программирования</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;На этот пост меня подвигли &lt;a href="http://blog.fogus.me/2011/10/02/the-elite-programming-language-fallacy/"&gt;рассуждения&lt;/a&gt; Майкла Фогуса (автора "The Joy of Clojure") о том, что такое "программерская элита".&lt;br /&gt;Фогус очень разумно объясняет, что не язык программирования является признаком "элитности" программиста. Даже Java может казаться сложной и непонятной тем, кто ее не знает. И даже Haskell будет простым и удобным тем, кто разобрался с этим языком.&lt;br /&gt;Более того, действительно крутые программисты, с которыми Фогусу довелось иметь дело, работали даже с таким позорным убожеством, как С++, и тем не менее умудрялись быть очень-очень продуктивными.&lt;br /&gt;Элитными могут быть, пожалуй, только программерские задачи, решить которые может лишь очень малый процент программистов. Т.е. не мега-навыки делают программиста "элитой", а, в первую очередь, крайне сложные проблемы, которые этот программист решает. Само собой, для решения сложных задач нужно иметь уровень профессионализма намного выше среднего, а также подходящие инструменты.&lt;br /&gt;Выбор языка программирования для решения нетривиальных задач -- это уже следствие сложности самих задач. Язык программирования тогда "подходит" для задачи, когда упрощает ее решение, а не тогда, когда конкретный программист знает язык лучше или любит его больше. Clojure -- один из таких языков. Его "функциональность", макросы, гомоиконный синтаксис, STM, REPL, Swank -- все это предназначено для упрощения решения именно сложных задач.&lt;br /&gt;Впринципе, согласен с тем, что написал Фогус. Clojure не просто располагает к экспериментированию, а прямо навязывает такой "экспериментаторский" стиль разработки. Вспомните, как нас учили программировать в школе и универе? Задачи часто решались на бумажке, нередко рисовали блок-схемы алгоритмов. Везде, где я работал, любили UML и много "планировали" перед тем, как приступить к кодированию. В Clojure -- все наоборот. Думать заранее, конечно, тоже нужно, но конспектируется "планируемое" решение не в блок-схемах или псевдокоде, а прямо на Clojure. &lt;br /&gt;Т.е. вначале может и возьмешь бумажку с ручкой, но уже через минуту думаешь: "Какого чёрта?", открываешь Emacs, Slime REPL и пробуешь все уже там. Заработало? Сохраняешь в файл с исходником. Не заработало? Пробуешь дальше, не перезагружая программу. Все вычисленные данные, все переменные -- все остается; прямо в работающей программе можно переопределить любые функции и значения. Создается ощущение, что попал не просто в debug-режим, а будто только в таком режиме и работаешь.&lt;br /&gt;На днях я экспериментировал с алогритмом Support Vector Machine, хотел научить классификатор по набору прилагательных от личать хорошие отзывы от плохих. С ресурса kinopark.by скачал все комментарии ко всем фильмам и рассортировал на два кластера: положительные отзывы и отрицательные (суммарно получилось окло 18Мб чистого текста). Выбрал все прилагательные (без окончаний) из каждого комментария; каждый комментарий закодировал дескриптором, содержащим 1 или 0 для каждого прилагательного из всего множества. Получился вектор размерностью около 10000. Вот такие-то вектора и скармливал классификатору на обучение.&lt;br /&gt;Все это я делал только исключительно на Clojure. Все это, включая загрузку и разбор страниц с комментариями, заняло около 300 строк кода. За все время мне ни разу не потребовался дебаггер, и даже в голову не пришло писать unit-тесты (зачем, ведь функция каждый раз "пробуется" в REPL-е перед попаданием в исходник). И я не могу себе представить инструмента, более удобного для таких задач, чем Clojure.&lt;br /&gt;&lt;br /&gt;Использованные библиотеки:&lt;br /&gt;1.clj-http; &lt;br /&gt;2.clj-tagsoup;&lt;br /&gt;3.libsvm.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-5119226318289861988?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/5119226318289861988/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2011/10/blog-post.html#comment-form' title='Комментарии: 18'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/5119226318289861988'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/5119226318289861988'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2011/10/blog-post.html' title='Элитные языки программирования'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><thr:total>18</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-5787698655511831956</id><published>2011-09-22T23:40:00.000+03:00</published><updated>2011-09-22T23:40:34.000+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='hiccup'/><title type='text'>Генерация Html в стиле Clojure</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Недавно прочел очень интересный &lt;strike&gt;срач&lt;/strike&gt; &lt;a href="http://www.reddit.com/r/programming/comments/al98b/clojure_and_compojure_to_the_rescue_again/"&gt;спор&lt;/a&gt; о том, как вообще true-программеры генерят html. Я даже не ожидал, что могу не знать такого интересного способа, хотя он лежит буквально на виду.&lt;br /&gt;Основные тезисы дискуссии ниже (оппонента так затроллили, что он от стыда удалил все свои комменты, и теперь там по сути одна гневная проповедь weavejester'a).&lt;br /&gt;&lt;br /&gt;1. &lt;i&gt;Html и css -- дизайнерам, код -- кодерам. И смотри не перепутай, Кутузау!&lt;/i&gt;&lt;br /&gt;Weavejester: "The problem with using HTML templating languages is that they quickly become impractical as the complexity of the page increases. Maintaining a file that's a mishmash of HTML from a designer and code from a developer is, in my experience, far more trouble than it's worth."&lt;br /&gt;&lt;br /&gt;2. &lt;i&gt;Html только для контента, весь look-n-feel делает CSS.&lt;/i&gt;&lt;br /&gt;Weavejester: "Secondly, I'm of the opinion that if the presentation of your HTML page  cannot be easily changed through CSS alone, then your HTML is probably  not well-designed. Rather than trying to construct HTML purely based  around a particular presentation layout, perhaps a better approach would  be to construct the HTML independent of the presentation, and then to  iteratively tweak this purely semantic structure into something that can  be easily altered by CSS into the desired design.&lt;br /&gt;...&lt;br /&gt;That's not to say all our HTML is perfectly formed straight off the bat.  Far from it. But when we find a part of our design can't easily be  altered by CSS alone, we try to fix the HTML so that it can be.  Eventually one gets to the point where the HTML rarely needs to be  altered, except when new content or features are added."&lt;br /&gt;&lt;br /&gt;3. &lt;i&gt;Дизайнер делает только layout. Весь контент, все внутренности html-а делает программист&lt;/i&gt;.&lt;br /&gt;Weavejester: "I don't disagree that the UI can change; I'm just of the opinion that  written correctly, you can (more or less) isolate the content from the  presentation. If anything, it's easier to change a set of well defined  functions, or a file of CSS, than it is to alter a clunky mashup of HTML  and inline code."&lt;br /&gt;vagif: "...designers make changes to html as opposed to developers by  saying that they are familiar with html and have the necessary tools.&lt;br /&gt;But this is awfully wrong. When you change html it is either  look'n'feel (colors,background,fonts,layout,icons,banners,etc) or  structural change (add / remove some elements like buttons, dropdowns,  edit boxes etc.)&lt;br /&gt;In first case it is never continuous work. You usually decide and  finish look'n'feel as one unit of work, and then just utilize the result  of designer's work by wrapping it in libraries/templates/includes so  developers would not waste their time to deal with those settings in  each particular page.&lt;br /&gt;Structural change on the other hand is where the actual bulk of  changes happens. And it is NEVER a designer's work, but always a  developer's. SO i was very surprised when you told that your designers  often change things "in a middle". What the heck do they change ? Color  schemes ? new banners every week ? you could not decide upon the layout  of your web site, so you change it that often ?"&lt;br /&gt;&lt;br /&gt;4. &lt;i&gt;True-way: дизайнер делает html-страницу, программер преобразовывает ее в набор функций, генерирующих этот самый html. После этого программер может редактировать полученный результат как код, а не как html&lt;/i&gt;.&lt;br /&gt;Weavejester: "As a developer, I'd much rather the designer send me a file of HTML I can convert into a concise set of functions."&lt;br /&gt;&lt;br /&gt;То есть я в корне неверно воспринимал hiccup! Этот движок темплейтов не используется для генерации страницы вручную. Дизайнерское поделие переводится в формат, который воспринимает hiccup, а затем программер может его редактировать как код, а не как html! Вот уж действительно true-разделение логики и представления!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Пример:&lt;br /&gt;&lt;br /&gt;&lt;pre style="background: #ffffff; color: black;"&gt;&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;ns&lt;/span&gt; webtest&lt;span style="color: #808030;"&gt;.&lt;/span&gt;core&lt;br /&gt;  &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;:require&lt;/span&gt; &lt;span style="color: #808030;"&gt;[&lt;/span&gt;pl&lt;span style="color: #808030;"&gt;.&lt;/span&gt;danieljanus&lt;span style="color: #808030;"&gt;.&lt;/span&gt;tagsoup &lt;span style="color: #400000;"&gt;:as&lt;/span&gt; ts&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: #808030;"&gt;[&lt;/span&gt;hiccup&lt;span style="color: #808030;"&gt;.&lt;/span&gt;core &lt;span style="color: #400000;"&gt;:as&lt;/span&gt; h&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: #808030;"&gt;[&lt;/span&gt;clojure&lt;span style="color: #808030;"&gt;.&lt;/span&gt;contrib&lt;span style="color: #808030;"&gt;.&lt;/span&gt;duck-streams &lt;span style="color: #400000;"&gt;:as&lt;/span&gt; io&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;def&lt;/span&gt; data &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;slurp&lt;/span&gt; &lt;span style="color: #0000e6;"&gt;"data/data.html"&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;def&lt;/span&gt; parsed &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;ts/parse-string&lt;/span&gt; data&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;def&lt;/span&gt; generated &lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;h/html&lt;/span&gt; parsed&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;io/spit&lt;/span&gt; &lt;span style="color: #0000e6;"&gt;"data/output.html"&lt;/span&gt; generated&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&amp;nbsp;В файле проекта должно быть:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre style="background: #ffffff; color: black;"&gt;&lt;span style="color: #808030;"&gt;(&lt;/span&gt;&lt;span style="color: maroon; font-weight: bold;"&gt;defproject&lt;/span&gt; webtest &lt;span style="color: #0000e6;"&gt;"1.0.0-SNAPSHOT"&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #400000;"&gt;:description&lt;/span&gt; &lt;span style="color: #0000e6;"&gt;"FIXME: write description"&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #400000;"&gt;:dependencies&lt;/span&gt; &lt;span style="color: #808030;"&gt;[&lt;/span&gt;&lt;span style="color: #808030;"&gt;[&lt;/span&gt;org&lt;span style="color: #808030;"&gt;.&lt;/span&gt;clojure&lt;span style="color: #808030;"&gt;/&lt;/span&gt;clojure &lt;span style="color: #0000e6;"&gt;"1.2.1"&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;br /&gt;                 &lt;span style="color: #808030;"&gt;[&lt;/span&gt;org&lt;span style="color: #808030;"&gt;.&lt;/span&gt;clojure&lt;span style="color: #808030;"&gt;/&lt;/span&gt;clojure-contrib &lt;span style="color: #0000e6;"&gt;"1.2.0"&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;br /&gt;                 &lt;span style="color: #808030;"&gt;[&lt;/span&gt;clj-tagsoup &lt;span style="color: #0000e6;"&gt;"0.2.6"&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;br /&gt;                 &lt;span style="color: #808030;"&gt;[&lt;/span&gt;hiccup &lt;span style="color: #0000e6;"&gt;"0.3.6"&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;span style="color: #808030;"&gt;]&lt;/span&gt;&lt;span style="color: #808030;"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;Надо сказать, что хотя результат незначительно отличался по размеру, в броузере сгенерированная html-страница выглядела абсолютно идентичной оригиналу.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-5787698655511831956?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/5787698655511831956/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2011/09/html-clojure.html#comment-form' title='Комментарии: 16'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/5787698655511831956'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/5787698655511831956'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2011/09/html-clojure.html' title='Генерация Html в стиле Clojure'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><thr:total>16</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-7363791987170085142</id><published>2011-09-13T20:09:00.000+03:00</published><updated>2011-09-13T20:09:31.178+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>Популярность Clojure растет</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-6fgH2vmc7Fg/Tm-OLbN1tlI/AAAAAAAAAGo/KNBtLLCeRqU/s1600/histo.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="205" src="http://1.bp.blogspot.com/-6fgH2vmc7Fg/Tm-OLbN1tlI/AAAAAAAAAGo/KNBtLLCeRqU/s320/histo.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;Сегодня, 13.09.2011, примерно в восемь вечера наблюдал одну очень интересную картину на irc.freenode.net. На канале #clojure было 345 человек, на #lisp -- 344, на #scheme -- 160. Забавно, не правда ли? Ведь и Clojure, и Scheme -- это все Lisp, но не каждый Lisp -- Clojure. Т.е., по идее, многие лисперы с каналов #clojure и #scheme сидят и на #lisp, но вот программисты Common Lisp врядли посещают #clojure и #scheme.&lt;br /&gt;&lt;br /&gt;Вывод: популярность Clojure у irc-чатлан как минимум сравнялась с Common Lisp и все набирает обороты. Особенно интересно еще и то, что где-то полгода назад на канале #clojure тусовалось в среднем примерно 200-220 человек, а на канале #lisp я наблюдал в основном около 330.&amp;nbsp;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-7363791987170085142?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/7363791987170085142/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2011/09/clojure_13.html#comment-form' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/7363791987170085142'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/7363791987170085142'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2011/09/clojure_13.html' title='Популярность Clojure растет'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-6fgH2vmc7Fg/Tm-OLbN1tlI/AAAAAAAAAGo/KNBtLLCeRqU/s72-c/histo.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-5245671062761036755</id><published>2011-09-12T10:58:00.001+03:00</published><updated>2011-09-12T11:05:57.048+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='общение'/><title type='text'>Впечатления о встрече разработчиков Scala</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;В субботу выступал на небольшой встрече разработчиков &lt;a href="http://www.scala.by/"&gt;Scala&lt;/a&gt;. Если в общем и целом: все было здорово, мне очень понравилось. Отдельное спасибо Василию Ременюку за классную организацию встречи. Было все, что нужно: удобное помещение, проектор, ноутбук и даже автомат с бесплатным кофе.&lt;br /&gt;&lt;br /&gt;Аудитория собралась образованная. После доклада о возможностях Lisp-а меня еще долго держали вопросами, причем не только грамотными, но и очень каверзными.&lt;br /&gt;&lt;br /&gt;Правда, как ни странно, хотя я и попытался разжевать во всех подробностях, что такое лисп и как он используется, все равно в разных вариациях услышал вопрос: "Ну нафиг нам твой Lisp со всеми этими скобочками? Есть ведь JetBrains MPS, Xtext, antlr, бла-бла-бла..."&lt;br /&gt;&lt;br /&gt;У меня встречный вопрос: а что вы чаще используете, паттерны или MPS? В Lisp-е я не задумываясь, влегкую, введу новые операторы, сделаю библиотеку паттернов и других общих архитектурных фрагментов; все будет DRY, никакого копи-пейста. И это будет все тот же самый язык, на котором написан основной код проекта.&lt;br /&gt;&lt;br /&gt;А вы такое сможете со своей Java, C#, С++, даже с использованием этих дополнительных утилит? И даже если сможете, то почему не делаете?&lt;br /&gt;&lt;br /&gt;Тут нет никакой загадки: любой external DSL всегда по определению сложнее в реализации и поддержке, чем аналогичный embedded. И чем сложнее DSL, тем заметнее разница в сложности реализации всей инфраструктуры external DSL (парсер, набор синтаксических правил и т.д.) и embedded DSL, для которого ничего дополнительно приделывать не нужно.&lt;br /&gt;&lt;br /&gt;Спасибо всем присутствовавшим, мне было интересно с вами пообщаться. Надеюсь, я заинтересовал вас языком Clojure, и через какое-то время появится наша Belarussian Clojure Users Group.&lt;br /&gt;&lt;br /&gt;Материалы моего выступления доступны &lt;a href="http://scala.by/meetups/2011/09/10/4.html"&gt;здесь&lt;/a&gt;.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-5245671062761036755?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/5245671062761036755/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2011/09/scala.html#comment-form' title='Комментарии: 38'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/5245671062761036755'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/5245671062761036755'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2011/09/scala.html' title='Впечатления о встрече разработчиков Scala'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><thr:total>38</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-1628454246636174194</id><published>2011-09-07T10:21:00.002+03:00</published><updated>2011-09-07T10:24:16.709+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='use cases'/><title type='text'>Clojure vs Scala - anecdote</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;В связи с моим предстоящим выступлением на встрече разработчиков Scala решил &lt;strike&gt;накинуть говна на вентилятор&lt;/strike&gt; перевести появившийся недавно Clojure use case. Оригинал появился сегодня рано утром и взят &lt;a href="http://groups.google.com/group/clojure/browse_thread/thread/b18f9006c068f0a0"&gt;отсюда&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;"Хотелось бы немного поделиться нашим опытом из World Singles...&lt;br /&gt;В ноябре 2009-го мы начали разрабатывать на Scala. Нам нужен был очень длительный процесс, который публиковал бы большие тома изменений из нашей БД пользователей в виде XML-пакетов, опубликованных в кастомном поисковом движке. Отобразить пол дюжины таблиц из БД на XML было довольно сложно, и компания уже перепробовала несколько решений с разным успехом. Я предложил Scala, основываясь на производительности, конкурентности, типобезопасности и краткости (особенно учитывая, что XML -- родная структура данных в Scala).&lt;br /&gt;Мы использовали публикующие демоны на Scala в продакшене больше двух лет. В основном, они работали неплохо, однако при высокой нагрузке часто выкидывали исключения Out of Memory. После длительного ковыряния в коде, мы пришли к выводу, что это происходило благодаря (по крайней мере, частично) дефолтной реализации акторов в Scala. Scala должна скоро объединиться с Akka, и, так или иначе, мы раздумывали о миграции на Akka...&lt;br /&gt;Но после того, как мы начали использовать Clojure в этом году (после экспериментов с Clojure с мая этого года) выяснилось, что довольно просто реализовать Clojure-версию нашего Scala-кода.&lt;br /&gt;Чтобы по-новой создать публикующего демона, потребовалось около 15-ти часов работы, после которых стали проходить все наши функциональные тесты. Теперь мы запускаем полный тест, публикующий более 300 000 профайлов за раз. Scala-код вылетает с OoM-исключением уже на 50 000 профайлов (иногда даже меньше). А Clojure-код вышел сухим из воды и спокойно работает на таких нагрузках. Теперь мы собираемся заменить наш Scala-код на Clojure-код в следующем продакшен-билде.&lt;br /&gt;Другая интересная особенность заключается в том, что Scala-код занял около 1000 строк (примерно 31k символов). Аналог на Clojure занял всего 260 строк и 11.5к символов. Ни тот, ни другой исходник не содержит большого числа комментариев (кгмм... я не горжусь этим, конечно, а просто показываю, что ни в одном из исходников нету лишнего текста, мещающего сравнивать их по объему). В сравнении также не учитывается код юнит-тестов, так что мы сравниваем чистый продакшен-код. Clojure-код написан по тем же принципам, что и Scala-код, в основном с теми же самыми функциями (Scala-код был написан в функциональном стиле), с небольшим рефакторингом для извлечения helper-функций и повышения модульности и упрощения поддержки.&lt;br /&gt;В результате, понятное дело, мы берем в продакшен Clojure-код и полностью отказываемся от Scala.&lt;br /&gt;Слава Ричу Хикки и Сlojure/core-команде за разработку такого великолепного языка общего назначения, решающего большие задачи! Спасибо вам!"&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-1628454246636174194?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/1628454246636174194/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2011/09/clojure-vs-scala-anecdote.html#comment-form' title='Комментарии: 32'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/1628454246636174194'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/1628454246636174194'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2011/09/clojure-vs-scala-anecdote.html' title='Clojure vs Scala - anecdote'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><thr:total>32</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-601701030227251429</id><published>2011-09-05T10:56:00.000+03:00</published><updated>2011-09-05T10:56:59.931+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='общение'/><title type='text'>Доклад про Clojure</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-pqncIyINbvg/TmR7pd5PSWI/AAAAAAAAAGY/nr4dIgRqtAU/s1600/images.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-pqncIyINbvg/TmR7pd5PSWI/AAAAAAAAAGY/nr4dIgRqtAU/s1600/images.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;В эту субботу, 10.09.2011, впервые буду делать доклад по Clojure на небольшой &lt;a href="http://scala.by/news/2011/08/29/meetup-4.html"&gt;коференции&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Тема доклада: "Предметно-ориентированные языки и Lisp как средство их построения".&lt;br /&gt;&lt;br /&gt;Приходите, и вы узнаете:&lt;br /&gt;1. Что такое предметно-ориентированные языки (Domain Specific Languages);&lt;br /&gt;2. Зачем, когда и как применять DSL;&lt;br /&gt;3. Что такое Lisp и зачем в нем столько скобочек;&lt;br /&gt;4. За что лисперы презирают паттерны проектирования;&lt;br /&gt;5. Почему почти невозможно похвастаться перед лиспером возможностями своего любимого языка программирования. &lt;br /&gt;&lt;br /&gt;Приглашаем всех желающих!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-601701030227251429?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/601701030227251429/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2011/09/clojure.html#comment-form' title='Комментарии: 18'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/601701030227251429'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/601701030227251429'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2011/09/clojure.html' title='Доклад про Clojure'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-pqncIyINbvg/TmR7pd5PSWI/AAAAAAAAAGY/nr4dIgRqtAU/s72-c/images.jpg' height='72' width='72'/><thr:total>18</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-3388291645553491285</id><published>2011-08-11T09:37:00.001+03:00</published><updated>2011-08-11T09:38:22.277+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Clojure: The Phantom Menace</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-r_y4ohyTixY/TkN3L-nAucI/AAAAAAAAAGQ/IjDPqJJS4e0/s1600/pm.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;br /&gt;&lt;/a&gt;&lt;/div&gt;&lt;a href="http://2.bp.blogspot.com/-l0tHcdUq8qg/TkN3pVvl7CI/AAAAAAAAAGU/Fa_hDB2Qjy0/s1600/pm.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-l0tHcdUq8qg/TkN3pVvl7CI/AAAAAAAAAGU/Fa_hDB2Qjy0/s1600/pm.png" /&gt;&lt;/a&gt;Еще буквально пару месяцев назад ситуация с веб-разработкой на Clojure была едва ли не плачевной. Чтобы разработать довольно простое приложение на Clojure нужно было здорово постараться, т.к. не было единого веб-фреймворка вроде того же Ruby on Rails.&lt;br /&gt;Однако, оказывается, Clojure развивается просто стремительно. Не так давно появился фреймворк &lt;a href="http://www.webnoir.org/"&gt;Noir&lt;/a&gt;, ориентированный на веб-разработку; недели две назад вышел &lt;a href="https://github.com/clojure/clojurescript"&gt;ClojureScript&lt;/a&gt;; с месяц назад хостинг &lt;a href="http://www.heroku.com/"&gt;Heroku&lt;/a&gt; объявил о поддержке Clojure. Смесь получилась ядерная.&lt;br /&gt;Во-первых, Noir -- достаточно модульная штука, которая позволяет пользоваться любимыми технологиями, Google Closure Templates, и забыть про ненавистный hiccup, используемый по-дефолту.&lt;br /&gt;Во-вторых, Noir предлагает более удобный DSL для маршрутизации, по сравнению даже с Compojure.&lt;br /&gt;В-третьих, это не любительская поделка, а небольшой стартап. Есть даже готовое &lt;a href="http://www.typewire.io/"&gt;приложение&lt;/a&gt; в продакшене, выполненное именно с применением Noir.&lt;br /&gt;Можно также найти и вменяемое &lt;a href="http://thecomputersarewinning.com/post/clojure-heroku-noir-mongo"&gt;руководство&lt;/a&gt; по размещению приложения Noir на хостинге Heroku.&lt;br /&gt;Еще недавно стала доступна библиотека &lt;a href="https://github.com/ibdknox/pinot"&gt;Pinot&lt;/a&gt;, упрощающая использование ClojureScript на клиентской части. Один из очень приятных её бонусов: Ajax из коробки.&lt;br /&gt;Конечно, до того же Ruby on Rails или Python Django еще далеко. Но это ничего, с такими темпами через год-другой Clojure станет очень серьезным конкурентом рубистам.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-3388291645553491285?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/3388291645553491285/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2011/08/clojure-phantom-menace.html#comment-form' title='Комментарии: 3'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/3388291645553491285'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/3388291645553491285'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2011/08/clojure-phantom-menace.html' title='Clojure: The Phantom Menace'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-l0tHcdUq8qg/TkN3pVvl7CI/AAAAAAAAAGU/Fa_hDB2Qjy0/s72-c/pm.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-5007439251620942939</id><published>2011-08-05T10:03:00.001+03:00</published><updated>2011-08-05T11:25:12.521+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>Clojure в production</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;i&gt;&lt;span style="font-size: small;"&gt;В последнее время наиболее интересная для меня тема -- кто, как и зачем использует Clojure&amp;nbsp; в реальных проектах. Хотя это язык общего назначения, не все типы приложений пишутся на нем так же легко, как на других языках. Зато кое-какие задачи решаются значительно легче, чем на тех же Java и Ruby. Ниже -- вольный перевод &lt;a href="http://www.coderanch.com/t/542287/clojure/Incubator/you-Clojure"&gt;поста&lt;/a&gt; о том, как используют Clojure в одной из платформ для сайтов знакомств.&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;"Я работаю на компанию знакомств по интернету. У нас была платформа, разрабатывавшаяся более десяти лет (до того, как я присоединился), и чтобы написать новую версию с нуля, мы потратили более восемнадцати месяцев. Сложность платформы такова, что конкурентное выполнение кода -- большая проблема, так что я хотел получить более чистый код и простую конкурентность -- функциональное программирование с неизменяемыми данными прекрасно для этого подходит. Изначально я предложил Scala для кое-какой низкоуровневой инфраструктуры с неизменяемыми данными и конкурентностью на акторах. Проблема Scala в том, что это типичный "&lt;span class="postbody"&gt;compile-deploy-debug-edit" язык, поэтому разработка шла довольно медленно (быстрее, чем на java, но все же это тот же "&lt;/span&gt;&lt;span class="postbody"&gt;compile, deploy, restart, &lt;a class="api" href="http://www.javaranch.com/unit-testing.jsp" target="_new" title="article: Evil Unit Testing"&gt;test&lt;/a&gt;, rinse, repeat&lt;/span&gt;&lt;span class="postbody"&gt;" язык); система типов Scala сложна для понимания средним программистом. В конце-концов выяснилось, что Scala не очень хорошо подходит для нашей команды, так что я решил поискать другой функциональный язык на jvm. Оказалось, что Clojure подходит идеально: он динамически типизирован, у него есть REPL для исследования, он легко вызывается из java; предлагает неизменяемые данные, безопасные в конкурентном выполнении, а также изменяемые данные в безопасном окружении (STM).&lt;/span&gt;&lt;br /&gt;&lt;span class="postbody"&gt;Мы переписали наш низкоуровневый управляющий код, применив Clojure, так что теперь мы могли использовать его из других языков. Затем мы разработали маленький простой псевдо-ORM на Clojure, чтобы заменить некоторые из наших библиотек и позволить прозрачно менять RDBMS на NoSQL используя простые структуры данных: словари (maps). Мы поставили наш Clojure-код на production пару недель назад, и он доказал свою гранитную стабильность (пара ошибок была, но они были в нашем коде, не в Clojure!). Мы только что добавили пул соединений (всего пара строк на Clojure).&lt;/span&gt;&lt;br /&gt;&lt;span class="postbody"&gt;Думаю, что со временем количество нашего кода на Clojure будет расти. До сих пор наш опыт показывает, что код на Clojure проще раз в десять, и это чистый, простой, тестируемый код, который намного легче написать и использовать повторно.&lt;/span&gt;&lt;br /&gt;&lt;span class="postbody"&gt;А как вы используете Clojure?"&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-5007439251620942939?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/5007439251620942939/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2011/08/clojure-production.html#comment-form' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/5007439251620942939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/5007439251620942939'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2011/08/clojure-production.html' title='Clojure в production'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4007342736133040943.post-1660544139663699473</id><published>2011-08-03T16:25:00.001+03:00</published><updated>2011-08-03T16:25:58.327+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>Lisp в мэйнстриме</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Love5an в своем блоге поднял важную &lt;a href="http://love5an.livejournal.com/366931.html"&gt;тему&lt;/a&gt; о применимости лиспа для повседневных задач. Главный вывод из его статьи: в лиспе мало библиотек, по сравнению с мэйнстримными языками, поэтому разрабатывать с его помощью обычные приложения сложно. Эта мысль перекликается с тем, о чем я писал в предыдущем посте, где искал идеальные задачи для лиспа.&lt;br /&gt;В принципе, я абсолютно и полностью согласен с love5an'ом: используя только лисповую инфраструктуру сделать настольное или веб-приложение довольно -- задача трудоемкая.&lt;br /&gt;Но не с Clojure! Оговорюсь: не используйте различные обертки над библиотеками java, используйте ифраструктуру java. Ring, Compojure, etc -- все это довольно бедные библиотеки, которые не идут ни в какое сравнение с тем же j2ee. Seasaw хоть и скрывает частично Swing, но не заменит ее полностью. Можно продолжать долго, так что если знаете java -- то просто используйте java API напрямую, Clojure это позволяет.&lt;br /&gt;Если обращаетесь к java API -- не используйте Emacs, возьмите IDE. Это действительно удобнее, т.к. IDE умеет делать интеллектуальное автодополнение кода. Самое лучшее, что я видел для Clojure, это Idea+LaClojure. Community-версия Idea бесплатна, так что скачать эту IDE легко.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-Z3zyt9FKFoc/TjlKngaV_XI/AAAAAAAAAGI/Y4FDNO-ZbKY/s1600/screen1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="254" src="http://1.bp.blogspot.com/-Z3zyt9FKFoc/TjlKngaV_XI/AAAAAAAAAGI/Y4FDNO-ZbKY/s400/screen1.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;Конечно, учитывая динамичность Clojure, интеллектуальное автодополнение не настолько точное, как для той же java, но, тем не менее, тоже значительно облегчает работу. По крайней мере не нужно постоянно обращаться к документации за элементарными вещами.&lt;br /&gt;Чисто для эксперимента решил попробовать написать UI для простого настольного приложения.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-YohIR_CzAUY/TjlLHXkgFqI/AAAAAAAAAGM/nBn9B7ei9Uk/s1600/screen2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="251" src="http://3.bp.blogspot.com/-YohIR_CzAUY/TjlLHXkgFqI/AAAAAAAAAGM/nBn9B7ei9Uk/s400/screen2.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;С применением LaClojure+Idea это оказалось почти так же просто, как и на обычной java.&lt;br /&gt;Таким образом, Clojure совмещает простоту и практичность Java с мощью и выразительностью Lisp-а. &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4007342736133040943-1660544139663699473?l=my-clojure.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-clojure.blogspot.com/feeds/1660544139663699473/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://my-clojure.blogspot.com/2011/08/lisp.html#comment-form' title='Комментарии: 13'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/1660544139663699473'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4007342736133040943/posts/default/1660544139663699473'/><link rel='alternate' type='text/html' href='http://my-clojure.blogspot.com/2011/08/lisp.html' title='Lisp в мэйнстриме'/><author><name>Dmitry</name><uri>http://www.blogger.com/profile/17434341697941378483</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp0.blogger.com/_5f2867SWJi0/R-u9NtNpoxI/AAAAAAAAAAg/qiiUbeZXsdw/S220/me.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-Z3zyt9FKFoc/TjlKngaV_XI/AAAAAAAAAGI/Y4FDNO-ZbKY/s72-c/screen1.png' height='72' width='72'/><thr:total>13</thr:total></entry></feed>
