Как стать автором
Обновить

Комментарии 55

Хорошая статья, спасибо.

Я бы ещё добавил, что в прямом соответствии с идеологией «Simple made easy» в Clojure просто потрясающе сделан интерфейс взаимодействия с коллекциями данных и вообще со структурами. Все коллекции обрабатываются единообразно за счёт абстракции «последовательности». Для доступа к элементам последовательностей есть много функций, обычных и высшего порядка, которые позволяют получить желаемый результат вне зависимости от того, работают ли они со списком, вектором или множеством (а иногда и словарём). Близкая к этому тема — ленивые коллекции, которые используются повсюду в языке. Они тоже являются «последовательностями», поэтому работа с ними не отличается от работы, например, с вектором, но при этом у них ленивая семантика.

Вообще стандартная библиотека Clojure великолепна. Особенно если сравнить её с библиотекой, например, Common Lisp. Единообразие, логичность и принцип наименьшего удивления во все поля, при этом мощность не страдает.
По поводу коллекций — такое чувство, что вы описываете C# LINQ. Это я не разжигаю, а просто хочу заметить, что подобные вещи далеко уже не в новинку.
Отчасти соглашусь. Удобная работа с коллекциями есть во многих языках. С другой стороны, последовательности в Clojure иммутабельны, в отличии от итераторов. Вполне законно сделать что-то вроде:

(use 'clojure.java.io)
(with-open [rdr (reader "/tmp/test.txt")]
  (let [lines (line-seq rdr)]
    (println "Строк" (count lines))
    (println "Символов" (reduce + (map count lines)))
    (println "Слов" (count (mapcat #(re-seq #"\w+" %) lines)))))


Тут последовательность одновременно иммутабельная и ленивая (считывание происходит когда мы считаем количество слов). Мы можем не боятся передавать такой «итератор» в другие функции, или даже помещать его в глобальные переменные. Ну и последовательности могут быть бесконечными + иммутабельными.

(defn- make-fib
  ([] (concat [0 1] (make-fib 0M 1M)))
  ([a b] (lazy-seq (cons (+ a b) (make-fib b (+ a b))))))

(def fibs (make-fib))

; считаем 500е число
(time (println (nth 500) fibs))

; числа уже просчитаны, и хранятся в виде односвязного списка
(time (println (nth 500) fibs))


В C# бесконечные коллекции можно реализовать и все операции LINQ, которые могут быть ленивыми, таковыми являются. Неизменяемость коллекции при foreach-итерации есть, неизменяемость элементов коллекции не гарантируется.

Замечу также, что на LINQ сильно повлиял Haskell, поэтому красота, простота и удобство функциональных языков там сохранены настолько, насколько это было возможно.
Согласен, LINQ мощная и полезная штука. И тут Clojure собственно мало отличается.

С другой стороны, в этом плане Clojure отличается от того же Haskell. Там map принимает список. Но для Haskell это совершенно не проблема ввиду тотальной ленивости. А вот, скажем, для других Lisp-ов уже вызывает некоторые неудобства. Там cons-ячейка строго определенная мутабельная структура. Для Clojure это любой объект, который реализует Java-интерфейс с 4 методами.

Коллекции, последовательности и функции для работы с ними в Clojure абстрактны. И реализуются они через Java-интерфейсы. И не стоит забывать, что коллекции персистентные, этого в LINQ нету.
> Коллекции, последовательности и функции для работы с ними в Clojure абстрактны. И реализуются они через Java-интерфейсы.

Ну в LINQ то же самое — если класс реализует IEnumerable или IEnumerable, то с ним можно работать с помощью LINQ или вызывая LINQ-методы напрямую. Хотя может я вас не так понимаю.
Я не противопоставляю LINQ и Clojure, наоборот. В Clojure если объект реализует ISeq или Seqable, то с ним можно работать при помощи стандартных функций. Подход одинаков. Но он отличается, например, от CL, Schema, Erlang и других.

Конечно, не в новинку. Lisp, 1958 год.
Очень интересная статья, давно уже испытываю интерес к лиспоподобным языкам, хочется что то попробовать в этой области. Но меня всегда тормозит такая штука, что не совсем понятно, куда его применять. Интересно было бы почитать, как Closure полезен в хозяйстве простого программиста.
Clojure — язык общего назначения, применять его можно куда угодно. На нём можно писать и полноценные вебсайты (причём очень удобно и просто, гораздо проще чем, например, с использованием Java-фреймворков), и довольно сложные вычисления (я писал на нём несколько не самых простых лаб по вычислительной математике, пришлось, правда, использовать много низкоуровневой магии чтобы была достаточная производительность, но ничего чересчур сложного там нет).

Интересный факт: в Clojure нет огромного стандартного стека для веб-приложений, такого, как, например, JSF+EJB3+сервлеты в джаве (хотя никто не мешает писать сервлеты на Clojure — я пробовал, и это гораздо лучше, чем на джаве). Хоть в джаве и есть много фреймворков для создания веб-приложений, но все они так или иначе основаны на JavaEE (за очень редкими исключениями), и все они достаточно объёмны и тяжеловесны и тащат огромное множество зависимостей вплоть до ещё более объёмных серверов приложений. Вместо этого в Clojure есть несколько слабо связанных слоёв, представленных одной или несколькими наиболее используемыми библиотеками, которые можно комбинировать в любом порядке. При этом каждая библиотека по отдельности минимальна по размеру и очень проста в использовании. Типичная структура веб-приложения на Clojure — Ring как HTTP-платформа (то есть слой общения с сетью, как правило, с использованием Jetty) + Compojure для роутинга запросов и в качестве основы для написания middleware + sqlkorma для абстракции работы с БД. По вкусу — один из множества шаблонизаторов (Enlive, Hiccup и другие). Простейшее standalone-приложение состоит из 2 файлов — project.clj для сборки Leiningen'ом и один файл с кодом обработчика HTTP-запросов, всё вместе — не более сотни строк. При этом общую структуру даже при развитии проекта практически невозможно испортить — пространства имён в качестве единицы инкапсуляции дают очень большую гибкость в построении и развитии архитектуры.

Также я вообще не видел, где можно так же удобно писать GUI-программы, как на Clojure, если только вы не испытываете отвращения к Swing — с помощью потрясающей библиотеки Seesaw. Seesaw предоставляет декларативное описание интерфейсов плюс очень здорово спроектированый набор функций и протоколов для работы практически со всеми функциями Swing, а также много всяких дополнительных мелочей, вроде базовой реализации FRP (Functional Reactive Programming) для интерфейсов — декларативное описание потоков данных в интерфейсе и реакций на события. Я думал, что на Swing невозможно писать GUI без боли, а оказывается, что на Swing можно писать GUI с большим удовольствием)
Спасибо за развернутый ответ, теперь понятно, куда копать
НЛО прилетело и опубликовало эту надпись здесь
Спасибо за статью.

> вся красота языка в его органичности, тонкой стыковке отдельных элементов в единое целое.
Полностью согласен. Это — прямое отражение гибкости и силы ума создателя языка. Поэтому мне так нравятся языки из семейства лиспов.
Кстати, интересно узнать, какие ещё есть языки, созданные под влиянием идеи единообразия, общей органичности и гармонии в средствах. Говорят, такими же приятными выглядят Ruby и Erlang. Так ли это? Есть ли что-то подобно простое и поэтому красивое, при этом используемое в реальной работе?
Например есть Scala: тоже JVM-ориентированный, функциональный но более прагматичный. Используется в реальной работе как минимум в Twitter и LinkedIn.
Наслышан о Скале как о реинкарнации C++. С++ вряд ли можно назвать гармоничным, красивым и тем более простым языком.
Интересное мнение, впервые о нем слышу :)
Я пытался перейти на Scala раза три (как до изучения Clojure, так и после). И да, меня не покидало чувство «это же C++ для Java». В смысле не набор конкретных возможностей, а подход в целом, большое набор фич, общая сложность и подобное.
Была одна статья, где его сравнили с С++, но это не так. Во-первых все же С++ язык более низкого уровня, во-вторых Scala — мультипарадигмальный функциональный язык. Ну и в-третьх Scala OOP отличается от Javaвского, а соответственно и С++шного.
> Наслышан о Скале как о реинкарнации C++

Звучит как «не читал, но осуждаю....»
Ну и больше слушайте сарафанное радио. Там еще и не такое расскажут…
Вы заметили, что Скале оценок я не давал, однако Вы мне приписали негативное мнение об этом языке? Слышал об отношении к ней от профессиональных разработчиков, которые посвятили ФП не год и не два и успешно разрабатывают на функциональных языках проекты мирового уровня. Если мнения таких людей для Вас не ценнее сарафанного радио, то Вас я точно слушать в этом вопросе не буду :)
>которые посвятили ФП не год и не два

Вот в этом и проблема. Scala не ФП язык. Вот поэтому многие и ошибаются на его счет.
И ваше высказывание выглядит примерно так:
«Слышал о новом методе удаления аппендицита от крутейшего стоматолога, и он считает что это не надо».
Немного передергиваю конечно, но это чтобы явно показать суть. Т.е. вроде как оба врачи, но занимаются разными вещами и стоматологу реально сложно оценить такие новшества, хотя он и крутой спец в своей области.

Scala очень удачно смешивает ООП и ФП.
Чистые функциональщики плюются, ибо это не по феншую и претит им.
ООП-щики плюются, потому как сложно и непривычно.
И только те, кто смог понять что это и как это использовать, нарадоваться не могут новым возможностям.
> Scala очень удачно смешивает ООП и ФП.
Вот об этом мне и говорили, поэтому и интересуюсь. С++ в своё время «удачно» смешивал процедурный С с ООП. В любом случае, Скалой не пользовался и осуждать не буду.
Моя любимая фишка Scala — статическая типизация с достаточно умным type inferrence, позволяющем не засорять код лишними указаниями типов.
Clojure, на сколько я помню, типизирован динамически?
Да, типизация динамическая. Хотя при работе с Java-классами опционально можно указывать типы. Поддерживается примитивный вывод типов.
(let [s (.toString [1 2 3])]
  (println (.length s)))  ; тут компилятор знает, что s - строка

(def x "123")
(println (.length ^String x))  ; явно указываем тип

Придумать что-то лаконичнее и красивее Brainfuck сложно.
Хотя лично мне не нравится его синтаксис, я бы заменил "." на "!", а "," на "?". Но это так, придирки.

С другой стороны, писать на Brainfuck не очень удобно, правда, попробуйте.
Не знаю, не знаю. Не нашёл пока причин, чтобы изучить Clojure.
Язык интересный. Я бы его изучил лишь бы для того что бы в очередной раз поломать себе голову. Очень многое из того что вы рассказали уже есть в таком прекрасном языке как Scala (и при этом в довольно приятном синтаксисе).

Еще вопрос об interopobillity. Это конечно здорово что язык JVM-ный и что можно использовать многие JVM библиотеки, но согласитесь, лучше всегда иметь idiomatic обертку, а не пулять null-и направо и налево. А поскольку язык, по понятным причинам, не на широкого пользователя, то такие обертки придется писать Вам самому, на каждый чих.

На последок, хотелось бы добавить, что я не начал бы серьезный проект ни на Clojure, ни даже на Scala (да будь Clojure/scala хоть в 1000 раз «круче» Java), потому что разработчиков найти очень сложно. Не каждый может себе позволить финты ушами как например Twitter.
Про Smalltalk и C++ тоже так говорили. Разработчиков мало, да и процедурный код не идиоматичный, библиотеки новые…
Еще вопрос об interopobillity. Это конечно здорово что язык JVM-ный и что можно использовать многие JVM библиотеки, но согласитесь, лучше всегда иметь idiomatic обертку, а не пулять null-и направо и налево. А поскольку язык, по понятным причинам, не на широкого пользователя, то такие обертки придется писать Вам самому, на каждый чих.

Обёртки на каждый чих придётся писать только если у вас либо много легаси-кода на джаве, с которым необходимо взаимодействовать, либо если у вас в проекте используется очень много java-библиотек, к которым никто ещё не написал обёртки. Не могу сказать, что это случай большинства проектов. Как правило, в проектах используются либо pure Clojure-библиотеки (та же Compojure или, например, Hiccup), либо уже готовые и обычно достаточно качественные обёртки (например, clj-time для Joda Time или Ring для Jetty).
При этом, если действительно будет нужно, писать обёртки для библиотек в Clojure не то что не сложно, а очень просто. Во-первых, это всё же не взаимодействие на бинарном уровне, как между компилируемыми языками, а в рамках одной платформы. Во-вторых, интероп в Clojure очень хорошо продуман. Прямо из Clojure можно генерировать обычные классы практически любой сложности, а те ограничения, что есть, абсолютно несущественны для обеспечения интерфейса к библиотекам. Можно сказать, что Clojure предоставляет настолько широкие возможности для взаимодействия с объектно-ориентированной средой Java, насколько это вообще возможно для не-ОО языка. В самых распространённых случаях создание класса, реализующего какой-нибудь интерфейс для сторонней библиотеки выглядит так, как реализация протокола в примере кода в посте.
Также если сама библиотека на Java сделана хорошо, то к ней и обёртки не особо могут быть нужны. Вызов методов синтаксически почти не отличается от вызовов функций, поэтому можно использовать непосредственно саму библиотеку, без обёрток.
НЛО прилетело и опубликовало эту надпись здесь
Как то одиноким осенним вечером решил познакомиться с этим языком и сделал что то невозможное ) За неделю написал веб приложение на 1000+ строк кода. Производительность была очень высокой, фичи выходили одна за другой, все казалось просто и ясно. Причем я писал не только на Clojure, но заюзал еще и ClojureScript. Сам язык я кажется понял всего за день, по крайней мере базовые его конструкции, настолько они мне показались органичными и естественными.
Я открыл для себя много вещей, о которых даже как то и не подозревал. Предыдущий мой опыт был связан в основном с Ruby/Php.
Все бы было хорошо, если не одно но, — пока мало проектов на языке и соответственно трудно найти работу.
Очень рекомендую ознакомиться.
Тут мы явно указали компилятору, что переменная a должна быть динамической, т.е. хранится внутри ThreadLocal. Это увеличивает производительность, поскольку в реальности нам не нужно переопределять большинство ссылок, а ThreadLocal накладывает дополнительные расходы.

Немного не понял. Так все-таки размещение во ThreadLocal увеличивает производительность или несет («накладывает») доп. расходы?
Изначально в Clojure все var-перменные хранились в ThreadLocal. Но в версии 1.3 сделали оптимизацию — теперь нужно явно указывать, хотим ли мы такое поведение, или нет. Добавили даже «защиту от дурака» — если забыть добавить ^:dynamic для переменной с именем *some-var-name* (т.е. со звездочками), то Clojure печатает в терминал предупреждение.

Там все-таки лучше поправить на «Это УМЕНЬШАЕТ производительность, поскольку в реальности»
В разделе что дальше не хватает ссылочек на хорошие книжки/ресурсы для изучение сабжа. А то как в анекдоте получается: хочу! хочу!
Для введения советую alexott.net/ru/clojure/clojure-intro/index.html (спасибо alexott за статью) и java.ociweb.com/mark/clojure/article.html

При изучении полезными будут ресурсы:
www.clojure.org/ — официальный сайт, документация есть, но, пожалуй, лучше учить не по ней.
learn-clojure.com/ — много различных ссылок, в том числе на книги.
clojuredocs.org/ — неофициальная документация по стандартной библиотеке, очень полезная вещь при поиске конкретной функции.

Очень советую www.4clojure.com/ — сайт с набором задачек (от тривиальнейших, до вполне объемных). Гораздо интереснее знакомится с языком на практике. Хотя там нету задач на многопоточность и т.п.
НЛО прилетело и опубликовало эту надпись здесь
Уточнение, относительно недавно они обновили документацию на clojure.org (для Clojure 1.5), и теперь она опять актуальна.
Я советовал учить не по документации не потому что она устарела. На мой взгляд, лучше начать с туториалов или книг.
Еще из сильных сторон Clojure — активное сообщество умных людей, интересующихся инновациями.

Например: LightTable, Twitter Storm.
Кто там говорил, что Perl трудночитаем?))
А вот это Вы зря. Чужой код на Clojure читается весьма хорошо.
Зависит от автора и его стиля (всякое бывает), но в целом читаемость у языка на очень хорошем уровне.
Мдааа. Я бы назвал этот язык не Clojure, а BrainFuck++ или BrainFuck#. Во времена пышного расцвета паскаля и бейсика этот язык смотрелся бы вполне органично. Сейчас этот язык смотрится, как сказал ТС, СТРАННО. Без реального углубления в него очень сложно понять, есть ли у него изюминка, но при современном выборе лично я даже не стану в него углубляться. Для каких то задач в вебе проще воспользоваться Perl/PHP, куски проекта на C++/Asm, что то на Java/JavaScript/Flash. Для программирования под локальные приложения — C++/Asm/Delpfi Очень сложно сдвинуть в первую очередь себя ради изучения языка, если не понимаешь, почему для проекта нужно выбрать именно его. Озвученные мной языки имеют огромнейшее сообщество, документацию, примеры исходников и соответствующую поддержку. Этот язык напоминает мне эсперанто и подобные синтетические языки. Язык, который держится на небольшой группке непонятно чем мотивированных людей, имеет мало шансов на жизнь и широкое использование крупными компаниями в своих проектах. Соответственно, время потраченное на его изучение до нужного уровня, скорее всего не будет сконвертировано со временем в достойную зарплату. Зачем учить довольно редкий язык? Ради хобби? Может лучше потратить время на бильярд, боулинг, плавание или велосипед? Талия будет стройней, да и профилактика геморроя в молодом возрасте никому не мешала.
НЛО прилетело и опубликовало эту надпись здесь
А почему Вы считаете, что я не знаком с функциональным программированием? На Erlang-е несколько проектов «для себя» писал. Но лично на мой взгляд, этот язык более «читабельный». Я начинал с паскаля, С, бейсика, ассемблера 86х. После этого пришлось использовать для тех или иных задач много других языков, большей частью интерпретируемых. Не зная подробного синтаксиса этих языков, даже на первый взгляд можно разобрать общий смысл куска кода. Поэтому переход с одного на другой занимает не очень много времени. Программы на вышеупомянутом Erlang-е относительно читабельны для человека, который видит их первый раз. Но Clojure меня совсем не порадовал.

К вопросу о личной эффективности можно относиться по-разному. Сам специалист может быть очень высокого класса и вполне трезво оценивать свой уровень. Но за него он попросит и больше денег. Руководителю проекта проще держать команду «рабочих лошадок» среднего класса. Они не будут требовать большой зарплаты, обычно они менее «творческие» личности, ввиду чего их результаты работы более предсказуемы. Поэтому при старте проекта адекватный тим-лид вместо 3-х экспертов, пишущих на экзотическом языке, скорее наберет 10 взаимозаменяемых кодеров на классическом С++ или Java. После окончания проекта проще найти людей в тех. поддержку. Иногда очень узкая и маловостребованная квалификация может обернуться работой в престижной компании с баснословной зарплатой. Но кушать нужно каждый день, поэтому более распространенные языки дадут больше шансов быстро найти достойную работу. В случае «затыков» при программировании на экзотическом языке, еще сложнее найти достойную поддержку для решения вопроса. Я сам иногда учил новый для себя язык, т.к. знал, что на нем эту задачу решить проще. Но для меня всегда распространенность языка приоритетней перед его функционалом. Это мое личное ИМХО, которое я никому не навязываю. И которое не претендует на истину.
Скажите, а Вы владеете слепым набором? Для его освоения нужно потратить время на обучение, и немало времени. На первых порах скорость будет очень низкая, будет жутко неудобно и непривычно. Зато результат себя окупает сполна! Хотя, если Вы за день набираете не так уж много текста, то потраченное время может окупится очень не скоро (или вообще никогда).

Примерно схожая ситуация с Clojure. Для некоторых людей (не для всех!) использование Clojure может дать выигрыш в скорости, но только в долговременной песпективе. Т.е. глупо надется, что вот за пару часов освоил азы языка, и бац — стал кодить в 10 раз быстрее.

А вообще, для разных задач разные инструменты. Так, для веб сайтов (fronend) вполне подойдет и PHP, для автоматизации задач администрирования — Perl/shell. Просто не надо пытаться распространить данные решения на все сферы только потому, что они привычны для Вас.
Я довольно быстро печатал двумя пальцами, поэтому переход на слепой метод печати принес незначительный выигрыш. Времени тоже заняло не очень много, примерно неделю. Но сравнивать слепой метод и язык программирования не очень корректно. Слепому методу неплохо бы научить всех пользователей ПК и общий выигрыш будет гарантирован. Это будет приносить пользу ежедневно и гарантированно. А экзотические языки лучше сравнивать с узкой специализацией. У меня 5 или 6 знакомых закончили университет по узконаправленной специальности физики. Один из них уехал то ли в США, то ли в Канаду на высокие заработки. Остальные оказались наравне с джамшутами-разнорабочими. Специальность если и есть, то в этом конкретном городе он нафиг никому не нужен. Нет спроса. Зато есть спрос на PHP, Perl, C++, Asm, 1C, Java и т.д. Один поехал на север слесарем в газодобычу. Другой окончил курсы на бирже труда, сейчас оператор башенного крана. Третий ходил по клиентам устанавливал Консультант+. Про остальных не знаю, дороги разошлись. Время, потраченное на то, что еще неизвестно, пригодится или нет — время украденное у жизни. Пока не будет у языка распространения на уровне флэша или 1С, лично я такой язык изучать не буду. Хотя каждый сам выбирает свои приоритеты и риски.
Так Вы мир не поменяете :)
Я человек семейный, мне важна стабильность и надежность дохода. Борьба с ветряными мельницами типа изменения всего населения не по мне. Хотя в соседнем посте предлагал поставить на дорогах камеры, спарить их с пулеметом и уменьшать количество правонарушителей.
Пока не будет у языка распространения на уровне флэша или 1С
У меня для вас плохие новости. И да, я знаю Clojure там занимает >50 место, просто не забываем про java interop.
Андрей, Ваши статьи — это лучшее, что я читал по Clojure на русском языке. Спасибо Вам огромное за них!
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории