суббота, 26 мая 2012 г.

Впечатления о ClojureScript

На прошлой неделе решил наконец ознакомиться с ClojureScript по-подробнее. До этого я смотрел презентацию Рича Хикки и некоторые другие скринкасты, успел "пощупать" ClojureScriptOne и почитать пару статей о том, как он (ClojureScript) крут. В общем, никаких особых сложностей с ним я не ожидал, поэтому начавшиеся приключения стали для меня откровенным сюрпризом.

Во-первых, мне не удалось создать browser-connected repl, соединенный со своей html-иной. Я выполнял простые туториалы шаг за шагом, и на этапе соединения у меня ничего не получалось. Тогда я запустил проект samples/repl, входящий в поставку ClojureScript. И все было хорошо до тех пор, пока я не попытался соединиться с веб-страницей. В результате, при помощи Дэвида Нолена и еще нескольких разработчиков ClojureScript выяснилось, что документация чуть-чуть не соответствует. После моих гневных отзывов её (документацию) наконец поправили, а я напишу здесь русский вариант туториала о том, как начать работать с ClojureScript.

1. Качаем с гитхаба последнюю версию ClojureScript.
2. Настраиваем emacs:
  a) Создаем файл browser-repl.sh с правами на выполнение:
#!/bin/sh

if [ "$CLOJURESCRIPT_HOME" = "" ]; then
  CLOJURESCRIPT_HOME="`dirname $0`/.."
fi

CLJSC_CP=''
for next in lib/: lib/*: src/clj: src/cljs: test/cljs; do
  CLJSC_CP=$CLJSC_CP$CLOJURESCRIPT_HOME'/'$next
done

java -server -cp $CLJSC_CP clojure.main -e \
"(require '[cljs.repl :as repl])
(require '[cljs.repl.browser :as browser])
(def env (browser/repl-env))
(repl/repl env)"

б) Прописываем в .emacs настройки для inferior-lisp'a:
(setq inferior-lisp-program "~/clojure/clojurescript/browser-repl.sh")

Здесь "~/clojure/clojurescript/browser-repl.sh" -- естственно, путь к созданному на шаге 2а файлу browser-repl.sh.

3. Добавляем clojurescript/bin в $PATH.

4. Заходим в каталог clojurescript/samples/repl, компилируем исходники: cljsc src/ > main.js

Файл main.js будет позднее использоваться в index.html

Обратите внимание на содержимое файла test.cljs. Он содержит такую строчку:
(repl/connect "http://localhost:9000/repl")
Именно она позволяет затем соединиться с Emacs-ом.

5. Файлы index.html, main.js и каталог out необходимо захостить на каком-нибудь веб-сервере. Я, например, просто создал каталог Tomcat7/webapps/my и сложил туда все это барахло. В моем случае страница index.html стала доступна по адресу localhost:8080/my/index.html.

Внимание! Хостить страницу обяхательно, просто открыв её как файл с диска вы  не сможете к ней подконнектиться из Emacs-а!

6. Запускаем Emacs, выполняем команду M-x inferior-lisp.

7. Вводим следующие команды:
(require '[cljs.repl :as repl])
(require '[cljs.repl.browser :as browser])
(def env (browser/repl-env))
(repl/repl env)


8. Загружаем/обновляем index.html в броузере. После этого repl в Emacs-е должен заработать. Например, вычисление (+ 1 1) должно выдать ответ 2. Если это произошло -- вуаля! у вас веб-консоль прямо в Emacs-е!


В моем примере index.html содержит следующий фрагмент:

      <div id="dates">
 <div>23.05.2012</div>
 <div>25.05.2012</div>
 <div>27.05.2012</div>
 <div>28.05.2012</div>
 <div>29.05.2012</div>
      </div>
 
Таким образом, команды на скриншоте изменяют размер шрифта и цвет текста первой даты.

Вообще, документация на ClojureScript достаточно бедная. Поэтому, вместо того, чтобы искать блог-посты и туториалы по использованию ClojureScript, я просто стал читать его исходники. Рекомендую для этого воспользоваться инструментом Marginalia. С Leiningen 2 это вообще просто.

1. В .lein/profiles.clj добавьте плагин lein-marginalia

{:user {:plugins [ [lein-marginalia "0.7.0"]
                   [lein-pprint "1.1.1"]
                   [lein-swank "1.4.4"]]}}

2. Создайте новый проект: lein new clj-docs
3. Скопируйте в катлог cljs-docs/src содержимое каталогоа cljorescript/src.
4. Выполните команду lein marg, и в каталоге docs будет создан файл uberdoc.html, который слева содержит комментарии к исходнику,  а справа -- сам исходник.


Еще стоит ознакомиться с Google Closure API -- тоже полезное подспорье в работе с ClojureScript.

Ну а в целом, ClojureScript мне очень понравился, и я надеюсь использовать его дальше в своих проектах.


6 комментариев:

  1. Это вы еще километровых трейсов на пустом месте не видели. У меня совершенно противоположные ощущения от недели использования cljs, да -- проект интересный, но жутко сырой, в частности содержательность ошибок как времени компиляции, так времени выполнения (уже в браузере) стремится к нулю. 'Cannot call method call of undefined'.

    ОтветитьУдалить
  2. Вообще, содержательность стектрейсов -- это больная тема всего Clojure, а не только ClojureScript. Лично я с этим никак не борюсь, просто пользую swank и slime, что позволяет мне писать программу достаточно небольшими фрагментами. Надеюсь, в ClojureScript эта методика тоже прокатит...

    ОтветитьУдалить
  3. Unknow, проект конечно сырой, но это потому, что это еще альфа-версия, разработка идет полным ходом.

    Dmitry, интересно, почему вы не использовали lein-cljsbuild? С ним, кажется, настроить минимальное рабочее окружение гораздо проще.

    Да, кстати, от стектрейсов хорошо помогает https://github.com/dnaumov/clojure-contracts ;)

    ОтветитьУдалить
  4. Вначале хотел разобраться с нуля, как пользоваться ClojureScript, без посторонних инструментов.

    Кстати, а чем тебе :pre и :post -кондишены не угодили, что аж целый фреймворк контрактов решил пилить?

    ОтветитьУдалить
  5. Блин, комментарий куда-то пропал.

    > Вначале хотел разобраться с нуля, как пользоваться ClojureScript, без посторонних инструментов.

    Тогда понятно. Ну, надеюсь в скором времени запилят нормальную поддержку clojurescript в слайме - вот тогда будет красота.

    > Кстати, а чем тебе :pre и :post -кондишены не угодили, что аж целый фреймворк контрактов решил пилить?

    Во-первых, сообщения об ошибках у :pre\:post на редкость неинформативны (совершенно непонятно, что именно и почему упало), а во-вторых, они очень ограничены в возможностях (нельзя задавать их отдельно от определения функции, нет инвариантов и т.п.). Кроме того, использование функций вместо сырых sexpr'ов, на мой взгляд, делает код более лаконичным.

    ОтветитьУдалить