воскресенье, 9 октября 2011 г.

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

Пришлось мне столкнуться по-службе с Clojure. Мои впечатления от знакомства:
  1. Первое, что бросается в глаза — это дурацкие [] и {} вместо расово верных (). Не скажу, что они упрощают чтение кода, ИМХО разница невелика. А вот написание усложняют (по крайней мере мне). Набирать закрывающие сущее мучение; вылазит вот такое ))}))]) безобразие и сиди считай скобки. Так недалеко и до JavaScript с его вечными });});});
  2. Зато, эти же самые [] и {} позволяют сделать конфиг с симпатичным синтаксисом почти-JSON.
    {
      :listen {
        :host "localhost"
        :port 8080
      }
      :db {
        :host "localhost"
        :port 5432
        :database "database"
        :user "root"
        :password "secret"
      }
    }
  3. Жутко неудобно писать императивный код. Мутабельность здесь не любят. Код, конечно, от этого становится чище, но времени на написание уходит чуть больше.
  4. Нет многострочных комментариев. Пришлось написать reader macro, добавляющий комментарии в стиле Common Lisp.
    (defn dispatch-reader-macro [ch fun]
      (let [dm (.get (doto
                       (.getDeclaredField clojure.lang.LispReader
                                          "dispatchMacros")
                       (.setAccessible true))
                     nil)]
        (aset dm (int ch) fun)))

    (defn read-comment [rdr pipe]
      (loop [s nil]
        (let [c (.read rdr)]
          (if-not (and s (= c (int \#)))
                  (recur (= c pipe))))))

    (dispatch-reader-macro \| read-comment)
    Единственное неудобство: нужно делать require во всех файлах, где эти комментарии используются.
  5. Очень понравился Leiningen. Собрать с его помощью jar-файл и запустить его на сервере оказалось очень просто. Сделано "для людей".

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

LinkFly комментирует...

По поводу 2) - не аргумент, равнозначный конфиг в CL
(
:listen (
:host "localhost"
:port 8080
)
:db (
:host "localhost"
:port 5432
:database "database"
:user "root"
:password "secret"
)
)

или даже можно читабельней ни разу не менее симпатичный:)

Дополнительный синтаксис усложняет кодогенерацию и вообще работу со списочными структурами, представляющими и код и данные.

Alex Ott комментирует...

Для работы со скобками очень удобно использовать paredit, или его аналоги в других редакторах.

Насчет мутабельности - к этому быстро привыкаешь, и как показывает практика, не так часто она и нужна. А вот с ленивостью - надо быть очень осторожным, до сих пор регулярно натыкаюсь на ошибки, особенно когда работаешь с каким-то явовским кодом внутри map или т.п.

andy128k комментирует...

@LinkFly
Разница в том, что пример на clojure можно eval-ить. Ну и закрывающие скобки в CL рука не поднимается переносить. :)

andy128k комментирует...

@Alex Ott
Не хватает терпения осиливать всевозможные плюшки emacs'а. Запомнил M-w и C-y и хватит :).

Мутабельность нужна очень редко вообще. Я даже на C++ старался писать иммутабельный код и чистые функции. Но я ж не настоящий сварщик, clojure взял в руки три дня назад. Не всё получается с первого раза написать правильно.

Ну и постоянно тянет всё переписывать, полировать код. Перфекционизм так и прёт. Написал doseq, переделал в loop/recur, потом заменил на reduce. Какое-то колесо сансары прям.

Alex Ott комментирует...

@Andy: ну paredit очень сильно облегчает работу, как минимум он тебе будет вставлять парные скобки, а потом уже можно и остальному научиться - я из него только пользуюсь Ctrl-arrow left/right, которые позволяют сдвигать скобки корректно

Andrew Kondratovich комментирует...

lazy-seq + java mutable api = butthurt

LinkFly комментирует...

Да можно же ведь макросы чтения использовать и eval'ить себе на здоровье если надо. Только вот не надо:)

andy128k комментирует...

@LinkFly
Не надо никаких макросов.
Мне самому даже "(list ..." роднее чем эти "{", "[". :)

Vsevolod Dyomkin комментирует...

leiningen — это пипец: "шаг вправо, шаг влево — попытка к бегству". Попробуйте, например, сделать такую простую вещь, как добавить путь к java-библиотекам, которые находятся в двух разных директориях (например, lib и libs).

Alex Ott комментирует...

@Vsevolod: Lein очень легко патчится и расширяется, а автор охотно принимает патчи. Я туда засунул всю нужную мне функциональность...

Andrew Kondratovich комментирует...

@andy128k
[...] - вектор
(...) - список
{...} - мэп
#{..} - множество

разные вещи =)

Михаил Филоненко комментирует...

[...] - список, с жесткими позициями
(...) - список,
{...} - список, нечетные позиции - ключи, четные позиции - значения, ну или список ячеек
#{..} - список, элементы повторяться не могут

Анонимный комментирует...

Для многострочных комментариев можно использовать макрос comment из коробки.

andy128k комментирует...

user=> (comment )))
nil
java.lang.Exception: Unmatched delimiter: )
java.lang.Exception: Unmatched delimiter: )

Анонимный комментирует...

user=> #| sadf |#
nil
user=> #| sadf |#|#
CompilerException java.lang.RuntimeException: Unable to resolve symbol: |# in this context, compiling:(NO_SOURCE_PATH:0)
nil
user=> #| sadf |#|#|#
CompilerException java.lang.RuntimeException: Unable to resolve symbol: |#|# in this context, compiling:(NO_SOURCE_PATH:0)
nil
user=>

andy128k комментирует...

Смешно. Вот только не по-делу совсем.
Этот comment то же самое что и (when false ...). Часто нужно закомментировать фрагмент кода с несбаллансированными скобками. А тратить время на скобки в комментарии -- это за гранью добра и зла.

turtle комментирует...

Мне больше автоматический gensym в стиле var# понравился.

Анонимный комментирует...

нужно проверить :)