Clojure язык программирования для чего

Содержание

Как устроен функциональный диалект Лиспа Clojure и почему использующие его программисты восхищаются им

Clojure — современный диалект Лиспа, язык программирования общего назначения с поддержкой разработки в интерактивном режиме. Сам язык с динамической системой типов поощряет функциональное программирование и упрощает поддержку многопоточности, а благодаря своей структуре может запускаться на платформах Java и JavaScript. При этом на Clojure работают уже, как правило, опытные разработчики, которые осознанно выбрали этот язык для решения своих задач. Мы попросили Clojure-разработчиков рассказать о том, как устроен этот язык программирования, какие задачи можно решать с его помощью и отличиях от других языков — функциональных, смешанных или императивных.

Clojure — это диалект Лиспа, в котором заложена похожая философия кода как данных — гомоиконичность, а также мощная макросистема. Принципы гомоиконичности позволяют использовать крайне небольшой синтаксис языка Clojure, в котором структура программы похожа на его синтаксис — поэтому для того, чтобы понять структуру программы, нужно просто прочитать текстовую разметку программы. Если прибавить к гомоиконичности и маленькому синтаксису реализацию макросов — инструкций, которые сообщают программе, какие именно действия нужно выполнять — получается достаточно мощный язык программирования.

Название языка Clojure созвучно слову «closure» – «замыкание», которое в абстрактной математике обозначает множества, замкнутые на самих себя. При этом язык Clojure не полностью функциональный — в нем есть и поддержка функций с побочным эффектом, например, функции ввода и вывода. Однако они не имеют своего состояния и служат, как правило, только для взаимодействия алгоритма с внешним миром.

Дмитрий Сотников, University Health Network: Мы можем взять студента и научить его делать что-то полезное на Clojure за пару недель

Вы изучали Clojure как первый язык или уже были программистом, когда решили его изучить? Есть ли смысл изучать Clojure в качестве первого языка?

Я начал программировать очень много лет назад — мой первый язык был Basic, потом были C++, C. Основные первые работы были на Java, делал backend Application. За всё это время я смотрел разные языки, в том числе даже работал с JS, потому что начал изучать системы фронтенда.

Оказалось, что многие языки имеют проблемы с координацией, когда речь начинает идти о многопоточности и мультипроцессинге — не ясно, как они должны работать, чтобы не было постоянных race condition.

Тогда меня заинтересовало, какие существуют подходы к решению проблемы многопоточности. Начал смотреть на функциональные языки — первый язык, с которым я работал, был Haskell. Я месяцев шесть его использовал — мне очень понравилось, у него совершенно другой подход к тому, как строить программы, как работает стейт-менеджмент. Но потом я понял, что найти работу хаскелистом достаточно сложно — все-таки это маленький рынок, поэтому я выбрал другой функциональный язык — Scala.

Со Scala тоже возникли сложности — я заметил, что большую часть кода писал как на Java, ведь он сочетает в себе возможности функционального и объектно-ориентированного программирования — на Scala тоже есть объекты, и там можно делать гибридный код. И, даже не осознавая этого, начинаешь делать то, к чему привык. При этом лет 12 назад в Scala все процессы были еще не налажены, поэтому я начал подбирать другой язык программирования — и выбрал Clojure.

Мне сразу понравилось, что Clojure — совершенно другой язык по сравнению с уже традиционными языками разработки, которые мы используем и к которым давно привыкли. Clojure довольно интерактивный язык — в нем есть возможность написать функцию и сразу из редактора запустить её и посмотреть, как она работает, проверить её.

Несмотря на отличия, к Clojure нужно привыкнуть — тогда можно понять, что этот язык достаточно простой и маленький. Когда я работал со Scala, приходилось что-то постоянно гуглить, искать на Stack Overflow — смотреть, как можно решить ту или иную задачу, выбирать между разными подходами. В Clojure всё работает намного проще и все подходы к решению задачи в общем похожи друг на друга — все, кто работают с Clojure, делают алгоритмы в более или менее одинаковом стиле.

Clojure очень легко читать, если ты уже выучил язык, плюс всегда можно поиграть с кодом, который пишешь — посмотреть, что он делает, поменять его. Это очень помогает изучать язык — например, в Java после написания программы постоянно приходится заниматься дебагом, это снижает мотивацию и может вообще уничтожить желание работать именно с языком. А когда можно постоянно взаимодействовать и коммуницировать с кодом во время написания — как будто бы разговаривать, как это и происходит в Clojure, — становится намного интереснее.

Какие есть особенности у Clojure? Какие очевидные плюсы и минусы у этого языка?

Плюсы Clojure

Самый большой плюс Clojure в его функциональности и неизменяемости функций. Ты точно знаешь, что функция выполняет, и что нет никаких изменений, которые она за собой тянет. Поэтому когда смотришь код большого проекта, можно просто взять его кусок и точно знать, что он делает и что выполняет. При этом не обязательно знать, что делает окружающий его код. В традиционных же языках созданный код меняет другой код, поэтому всегда нужно понимать, как части программы взаимодействуют друг с другом, за что отвечают те или иные куски кода и как все устроено внутри алгоритмов. Именно поэтому в Java постоянно используют дебагер — ведь всю программу в голове держать невозможно, но нужно всё контролировать и прогнозировать все возможные изменения в коде после того, как разработчик решит поменять какую-нибудь строчку.

В Clojure можно просто прочитать код и узнать, что он делает и за что отвечает. И в его работе не будет никаких неизвестных изменений, которые он за собой тянет. Особенно это удобно, когда работаешь в большой команде — сам язык позволяет создавать изоляцию между программистами и один участок кода никак не может испортить другой.

Ещё из плюсов — в Clojure можно использовать любые библиотеки из Java и JavaScript. Это огромный плюс языка — сам по себе он достаточно маленький и людей на нем работает не очень много. Поэтому отлично, что можно использовать сторонние системы.

Мы работали и с Java, и с Clojure, но пришли к выводу, что одни и те же проекты на Clojure создаются намного быстрее. Нужно меньше кода, чтобы создать программу, он будет быстрее работать, а разработчик только решает проблему через язык программирования. А не как у JS или Java, где приходится много работать именно со структурой языка, а не с решением проблемы.

В программах, которые сделаны на Clojure, меньше багов. Когда ты приходишь в проект, где работают с Clojure, разбираться с кодом намного проще. В этом Clojure выигрывает у многих языков — особенно в работе с большими системами данных.

Минусы Clojure

Clojure достаточно сильно отличается от популярных языков разработки, к нему нужно привыкнуть, к его подходу. Ещё Clojure в некоторых местах немного медленнее, чем другие языки. Обычно это не проблема, но если у вас мало ресурсов, то лучше использовать что-то другое.

Другой минус — Clojure компилируется только на JVM или JS. Если эти платформы подходят к вашему проекту, то всё идеально, если нет — придется выбрать другой язык.

Clojure — динамический язык. Дело вкуса, но если вам не очень близок такой подход, и вы любите, например, делать статические переменные, то тоже лучше поискать что-то ещё.

Как устроено коммьюнити вокруг Clojure?

Коммьюнити в Clojure очень активное — например, у нас в Торонто недавно была большая конференция Clojure/north, которую мы делали, и в ней принимали участие программисты из абсолютно разных частей света — из Финляндии, США, Великобритании и даже Индии. Ну и есть, конечно, российские компании, которые используют Clojure.

При этом нужно понимать, что Clojure — нишевый язык, поэтому количество людей, которые его используют, никогда не сравнится с JS или Java. Но из языков поменьше, Clojure — один из самых востребованных, особенно из функциональных языков. Поэтому его используют многие компании, даже корпорации, включая Apple или Amazon.

Этот язык привлекает очень опытных людей, что влияет и на коммьюнити, и на всю экосистему вокруг языка. JS привлекает неопытных людей, которые в итоге создают не очень качественные библиотеки — они часто ломаются, потому что собирали их программисты без большого количества опыта. На Clojure, к счастью, всё устроено иначе.

Другая важная новость — компанию Congitect создателя Clojure Ричарда Хикки, которая занималась поддержкой и развитием языка, купила большая финансовая корпорация Nubank из Бразилии — она активно использует Clojure. Поэтому теперь язык будет ещё стабильнее и быстрее развиваться.

Существует ли спрос на программистов, которые используют Clojure? В сочетании с какими языками?

Обычно ищут разработчиков, которые уже знают JS или Java — все-таки Clojure использует инструменты и системы этих языков. И если человек знает уже платформу, то ему, конечно, будет легче.

Но вообще все зависит от компании — мы периодически берем студентов из университета, которые вообще не писали коммерческого кода. То есть мы можем взять студента и научить его делать что-то полезное на Clojure за пару недель. Clojure просто учить людям, у которых абсолютно нет опыта в программировании и голова пока не забита разными концепциями.

Выучить Clojure просто для людей, которые только начали программировать — с опытом до двух лет, либо уже опытным инженерам, которые программируют много лет. А в среднем — разработчикам, которые программируют от двух до пяти лет — обычно труднее всего. Когда разработчики делают всё по одному сценарию, а потом им говорят — можно задачу совсем иным подходом, — обычно человеку бывает сложно перестроиться. И тут дело именно в опыте разработки — он уже есть, но ещё не накопился до той критической массы, когда у программиста меняется подход к изучению нового.

Какие проекты лучше всего реализовывать на Clojure?

Clojure подходит для фуллстек-разработки, как и JS. Только Clojure будет намного более функциональнее. Да, существует Node.Js, но это достаточно медленный инструмент, который в скорости работы точно уступает Clojure.

Clojure лучше всего работает для финансового рынка, дата-аналитики, машинного обучения — сфер, где основной фокус делается на данных, которые нужно быстро обрабатывать.

Однако для веб и фронтенда Clojure тоже отлично работает, даже через собственные библиотеки, построенные на React. Знаю примеры, когда разработчики даже делали игры на Clojure — точнее на нём делалась логика игры, все остальное, конечно, писалось в Unity.

Будет ли Clojure востребован в будущем?

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

Михаил Клишин, VMware: Clojure — это Porsche мира языков программирования

Вы изучали Clojure как первый язык или уже были программистом, когда решили его изучить? Есть ли смысл изучать Clojure в качестве первого языка?

Для начала я хотел бы прояснить, что активно использовал Clojure несколько лет — с 2011 по 2015 года. С тех пор лишь поддерживаю с десяток своих open source библиотек в той степени, в которой позволяет время.

Clojure для меня даже не был первый Lisp-ом, первым был Emacs Lisp. Изучать Clojure как первый язык имеет смысл, только если ваша работа как-то будет связана с данными. Если вы хотите разрабатывать интерфейсы, конечно, можно начать с ClojureScript, но куда практичнее будет JavaScript или TypeScript.

Программисту, который работает с другими языками, более распространенными, полезно освоить Clojure для профессионального роста?

Как именно Clojure помогает прокачаться разработчику, за счёт чего это происходит?

Clojure, философия её автора и сообщества открывают глаза на то, что очень маленький набор базовых концепций полностью достаточен для решения огромного количества задач. В мире хватает языков программирования, где кардинально больше концепций, и каждый несколько лет добавляются новые, а старые редко удаляются или пересматриваются. Некоторые языки так набиты фичами и частными случаями, что даже опытные люди и авторы языка по-настоящему понимают и используют лишь какую-то его часть.

После знакомства с Clojure вы перестанете думать, что это — единственный возможный вариант, и станете искать более простые решения во всём.

Какие есть особенности у Clojure? Какие очевидные плюсы и минусы есть у этого языка?

Плюсы

Immutability (очень важная вещь для языка, ориентированного на concurrency, на мой взгляд), отличная стандартная библиотека для работы с коллекциями. «Расширяемый» полиморфизм. Метапрограммирование может быть полезно, а может быть и нет.

Читайте также:  Queen школа английского языка

Именно из-за минималистичности у Clojure несколько портов под разные рантаймы, и они действительно поддерживаются и используются.

Язык невозможно оценивать без какого-то мнения о его сообществе. Мне Clojure-сообщество нравится относительной зрелостью и отсутствием гонок за модой. Моим самым старым Clojure библиотекам 9 лет и я могу вспомнить лишь один раз, когда мне приходилось тратить время из-за изменений в языке. Backwards compatibility здесь одна из лучших в индустрии, и это опять же следствие компактности языка.

Минусы

Это нишевый и необычно выглядящий язык. Из-за этого некоторые люди и команды даже не рассматривают его. Это сужает количество потенциальных пользователей.

Компилятор Clojure иногда выдает совершенно неочевидные для новичков ошибки. Иногда это раздражает и опытных пользователей.

Лично на мой взгляд, многие ключевые люди в Clojure core team совершенно не ценят качество документации, а процесс контрибуций много лет требовал отправки бумажной почты (. ) в Durham, NC. За последние годы в этих вещах произошел сдвиг, но некоторые люди в сообществе выгорели в процессе борьбы за эти изменения и покинули его.

Компиляция в Clojure происходит при запуске приложения, что на JVM делает время старта очень печальным. Это решаемо, но делает Clojure менее конкурентноспособным для ряда задач.

Почему из всех функциональных языков вы выбрали именно Clojure, а не, например, Haskell?

У меня за плечами пять-шесть функциональных языка за последние десять лет. Где-то в 2011 году у меня за плечами было года полтора использования Scala, до этого — несколько лет Java, Ruby. Мне хотелось что-то объединяющее Java и Ruby, но экосистема Scala на тот момент была на стадии становления с жуткими growing pains. Так, например, при выходе новой версии в 2011 году надо было ждать рекомпиляции по сути всей экосистемы, потому что изменения в компиляторе были почти всегда обратно-несовместимыми (binary incompatible).

На все эти адаптации тратилось большое количество времени. Clojure, как я упоминал выше, является полной противоположностью в этом смысле. Поэтому я решил попробовать его и остался очень доволен. Справедливости ради отметим, что тех пор в Scala многое поменялось, но история с обратной совместимостью по-прежнему далека от идеала.

Haskell — язык, позволяющий глубже понять программирование.

Как он устроен и почему его выбирают разработчики? Подробно разобрали, как устроен чистый функциональный язык программирования и отличиях этой парадигмы разработки от других.

Последние годы я в основном работаю с Erlang и Elixir. Это тоже небольшие функциональные языки с immutable data structures и своим подходом к concurrency. Elixir многое почерпнул у Clojure. Например, первый автор Mix — в прошлом один из ключевых авторов Leiningen. Если бы не отдельный runtime и экосистема, Elixir был бы очень сильным конкурентом Clojure по всем фронтам, и с куда более открытым автором и сообществом.

Какие инструменты или фреймворки языка вы используете? Какие проекты лучше всего реализовывать на Clojure?

Мне Clojure кажется отличным вариантом для работы с данными, (микро)сервисами, где важна concurrency, или проблему хорошо решил бы DSL. Не уверен, что для UI-приложений рассмотрел бы ClojureScript: в этой сфере очень много альтернатив с огромными сообществами.

Есть ли спрос на программистов со знанием Clojure? В сочетании с какими языками и инструментами чаще всего работодатели ищут Clojure-разработчиков?

Сразу оговорюсь: я очень плохо представляю себе реалии спроса на навыки и найма инженеров на территории бывшего СССР. Все рассуждения ниже — в контексте мирового рынка.

На любой нишевый язык спрос не так велик, но иногда предложение даже меньше. На сегодняшний день Clojure в больших компаниях уже не удивителен, но основной спрос скорее со стороны стартапов. Масштабы спроса оценить не берусь, но знаю людей, которые хотели работать в основном с Clojure и довольно быстро к этому пришли. В индустрии определенно есть спрос на людей, открытых новым идеям и владеющих разными языками, и опыт с Clojure определенно будет плюсом с этой точки зрения.

Будет ли Clojure востребован в будущем?

Здесь уместно вставить XKCD про Lisp 😉 Люди каждые десятилетия открывают для себя диалект Lisp-а. Этому семейству языков более 50 лет, и Clojure является очень практичным и современным вариантом. Думаю, и язык, и нишевый спрос, останутся. Clojure — это Porsche мира языков программирования в том смысле, что это вряд ли будет вашим единственным рабочим языком. Но в правильной команде и с подходящим use case-ом он может дать отличный результат.

Николай Рыжиков, Health-Samurai: Clojure — это венец динамических языков

Вы изучали Clojure как первый язык или уже были программистом, когда решили его изучить? Есть ли смысл изучать Clojure в качестве первого языка?

Я уже был полиглотом — Visual Basic, PHP, JS, Ruby, Java, C#, когда познакомился с Clojure. Конечно, Clojure хорош как первый язык, но вместе с ним все-таки придется разобраться и с Java, и с JavaScript.

Изучение Clojure позволит разработчику выйти на новый уровень, глубже понять программирование?

По моему мнению, Clojure — это венец динамических языков. Ричард Хикки создал Clojure с третьего раза в профессионально-сознательном возрасте. Clojure отличается простым концептуальным дизайном и этому стоит поучиться.

Как именно Clojure помогает прокачаться, за счёт чего это происходит?

Clojure позволяет работать с сутью решаемой проблемы, не отвлекаясь на «ритуалы». REPL и функциональная основа языка взаимодополняют друг друга — вы интерактивно пишете и исполняете простой код, не покидая emacs — и это много стоит!

Какие есть особенности у Clojure? Какие очевидные плюсы и минусы есть у этого языка?

Clojure рассчитан на профессионалов, понимающих, что и зачем они делают. Не стоит ожидать, что вас поведут за ручку добрые фреймворки — придется думать своей головой.

Почему из всех функциональных языков вы выбрали именно Clojure, а не, например, Хаскель?

Хаскель слишком долго компилируется 🙂 Хаскель состоит из двух парадигм — функционального программирования и программирования на типах. Причем вторая часть толще, сложнее и бесполезнее, даже порой вреднее, для большинства задач.

Какие инструменты или фреймворки языка вы используете?

В Clojure предпочитают библиотеки фреймворкам. Где-то треть библиотек мы себе разработали сами. Интересный феномен — если в Clojure библиотеку уже не коммитили около года, это не значит, что она мертва. Возможно она просто закончена и больше нечего добавить.

Какие проекты лучше всего реализовывать на Clojure?

Clojure прагматичный язык общего назначения, на котором прекрасно пишется бизнесовые back и front, и даже системные вещи, типа баз данных (datomic).

Есть ли спрос на программистов со знанием Clojure? В сочетании с какими языками и инструментами чаще всего работодатели ищут Clojure-разработчиков?

Мы нанимаем на постоянной основе. Речь не идет о программисте на «X» — мы (и другие сознательные конторы) ищем инженеров с «правильным» мышлением, умеющих эффективно, просто и элегантно решать сложные проблемы. А на Clojure это делать приятно и легко.

Будет ли Clojure востребован в будущем?

В раскрутку Clojure, в отличии от многих других «корпоративных» языков, никто не вливал много денег. Это крепкое и пассионарное сообщество профессионалов, которое растет естественным образом. Те, кто пришли к нам несколько лет назад, уже внедрили Clojure в production — и это залог будущего для языка. Я не вижу пока никаких признаков заката, скорее стабильный нехайповый рост — и это хорошо для экосистемы.

Анализ рынка Clojure-специалистов

По сравнению с разработчиками, которые пишут на JS или Python, спрос на Clojure-программистов намного ниже. Несмотря на это, на HH есть вакансии, где требуются именно инженеры, которые смогут писать на этом языке программирования. На HH на сегодняшний день есть 12 вакансий Clojure-разработчиков, а на «Хабр.Карьере» — 2 вакансии, обе от компании Health Samurai.

Средняя зарплата Junior Clojure Developer, которую предлагают компании, составляет от 70 тыс. до 150 тыс. рублей. При этом в Health Samurai отмечают, что первоначальные знания Clojure не являются обязательными.

Вакансии уровня Senior для программиста со знанием Clojure или желанием погрузиться в изучение Лисп-языков, начинаются от 270 тыс. рублей в месяц.

На Хекслете сейчас нет курсов изучения Clojure, однако есть отдельный курс по функциональному программированию для JavaScript, вебинар на эту тему от разработчика Никиты Соболева или программиста Александра Гранина, бесплатный курс по языку Racket и небольшой курс по функциональному языку программирования Erlang.

Документацию к Clojure вы можете посмотреть на официальном сайте языка.

Кроме того, мы используем Clojure в open source проекте Codebattle, если хотите поучаствовать и попробовать изучить Clojure, то пишите нам.

Источник

Введение в Clojure

В статье рассказывается функциональном языке Clojure, работающем на платформе JVM. Данный язык имеет интересные возможности, которые упрощают разработку многопоточных программ для платформы JVM и может быть интересен для программистов использующих данную платформу. В статье мы старались избежать требований к наличию знаний о семействе языков Lisp, но это не всегда получалось. Однако мы надеемся, что статья все-таки будет интересна для программистов на Java, которые смогут воспользоваться возможностями языка в части конкурентного программирования.

Первоначально, данная статья была опубликована в 4-м номере журнала «Практика функционального программирования», но данная версия регулярно обновляется, отражая прогресс в развитии языка. Последний раз эта статья обновлялась для Clojure 1.2, и она не описывает новые вещи введенные в последующих версиях!

Что такое Clojure?

В отличие от других реализаций Lisp’а и Scheme для виртуальной машины Java, таких как ABCL, Kawa и т.д., Clojure не совместим на 100 процентов ни с Common Lisp, ни с Scheme, но позаимствовал многие идеи из этих языков, добавив новые вещи, такие как неизменяемость данных, конкурентное выполнение кода и т.п. Более подробно о том, зачем был создан новый язык, можно прочитать на сайте проекта.

Несмотря на то, что Clojure — молодой язык программирования, достаточно много людей используют его в своих проектах, в том числе и коммерческих, например, FlightCaster, который использует Clojure при обработке большого количества данных, решая задачи Machine Learning в распределенной среде. Существуют и другие фирмы (например, Sonian, Runa, Emendio), использующие этот язык в своей работе — ссылки на них вы сможете найти на сайте языка.

Основные возможности языка

Clojure является функциональным языком программирования с поддержкой функций в качестве объектов первого класса (first class objects) и неизменяемыми (за исключением специальных случаев) данными, включая поддержку «ленивых» коллекций данных. От Lisp’а Clojure «унаследовал» макросы, мультиметоды и интерактивный стиль разработки, а JVM дает переносимость и доступ к большому набору библиотек, созданных для этой платформы.

Неизменность структур данных позволяет использовать их в разных потоках выполнения программы, что упрощает многопоточное программирование. Однако не все структуры являются неизменяемыми — в нужных случаях программист может явно использовать изменяемые структуры данных, используя Software Transactional Memory (STM), что обеспечивает надежную работу в многопоточной среде. (В качестве примера многопоточной программы, работающей с разделяемыми данными, можно привести программу «муравьи» (ants), которую достаточно сложно написать на Java из-за большого количества моделируемых сущностей, но которая достаточно просто выглядит на Clojure).

За счет того, что Clojure был спроектирован для работы на базе JVM, обеспечивается доступ к большому набору библиотек, существующих для данной платформы. Взаимодействие с Java реализуется в обе стороны — как вызов кода, написанного на Java, так и реализация классов, которые доступны как для вызова из Java, так и из других языков, существующих для JVM, например, Scala. Подробнее о взаимодействии с JVM написано далее.

Отличия от Lisp

Полный список отличий можно найти на отдельной странице на сайте языка. Из явных отличий от Common Lisp можно отметить следующие:

Источники информации о языке

Основной источник информации по данному языку — сайт проекта и список рассылки. Помимо сайта проекта, хорошим источником информации является набор видеолекций на Blip.TV, а также видеолекции, в которых автор языка рассказывает о Clojure и об особенностях его использования. Кроме того, следует отметить набор скринкастов, созданных Sean Devlin, в которых он рассказывает о разных возможностях языка, включая новые, появившиеся в версии 1.1.

Из книг в настоящее время доступна книга Programming Clojure, выпущенная в серии Pragmatic Programmers, которая в принципе содержит всю необходимую информацию о языке, включая описание основных возможностей языка, вопросы взаимодействия с Java, основные функции, отличие языка от Common Lisp, и т.п. В мае 2010 года издательство Apress выпустило еще одну книгу по Clojure — Practical Clojure. The Definitive Guide, которая является кратким описанием современной версии языка, включая новшества, которые введены в версии 1.2. А на начало 2011 года в издательстве Manning запланирован выход книг Clojure in Action (введение в язык и примеры практического использования) и The Joy of Clojure. Thinking the Clojure Way (более «глубокое» описание языка, с разъяснением сложных понятий).

В свободном доступе можно найти книгу Clojure Programming, работа над которой ведется в рамках проекта WikiBooks. Также существует достаточно подробный практический учебник — Clojure Scripting. Кроме того, недавно был опубликован учебник Clojure Notes, который использовался в рамках курса обучения Clojure.

Хорошее описание того, как можно использовать макросы для построения абстракций, можно найти в известной книге On Lisp Пола Грэма (Paul Graham). Несмотря на то, что в ней используется Common Lisp, многие вещи будут применимы и для Clojure. 2

Очень большое количество информации о языке, разрабатываемых библиотеках и проектах, использующих Clojure, публикуется в блогах. Для того, чтобы свести всю эту информацию воедино, существует проект Planet Clojure, на который вы можете подписаться, чтобы быть в курсе новостей о языке.

Установка и запуск

Установка Clojure достаточно проста — скачайте последнюю версию с сайта языка и распакуйте в нужный каталог. После этого вы можете запустить ее с помощью команды:

Из чего состоит язык Clojure

Синтаксис языка Clojure следует стандартному для Lisp’образных языков подходу «код как данные», когда данные и код имеют общий синтаксис. Как и в других диалектах Lisp’а, код записывается в виде списков, используя префиксную нотацию и представляя собой синтаксическое дерево. Однако по сравнению с другими языками, в Clojure введены дополнительные сущности: кроме стандартных для Lisp’а символов, базовых литералов (строки, числа и т.п.) и списков, в язык введен дополнительный синтаксис для векторов, отображений (maps) и множеств (sets), являющихся объектами первого класса (first class objects).

Читайте также:  Изъяснительные придаточные в русском языке

Кроме этого, процедура чтения кода (reader) распознает специфические для Clojure конструкции: @ — для доступа к изменяемым данным и различные конструкции, начинающиеся с символа # — анонимные функции, метаданные (включая информацию о типах данных), регулярные выражения и т.д. Процедура чтения также рассматривает пробелы и запятые между элементами языка как один символ, разделяющий эти элементы.

Основные типы данных

Все типы данных имеют общий набор характеристик: данные неизменяемы и реализуют операцию «равенство» (equality).

Базовые типы данных

К базовым типам данных Clojure относятся следующие:

Коллекции, последовательности и массивы

Кроме общих характеристик базовых типов перечисленных выше, все коллекции в Clojure имеют следующие характеристики:

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

В текущей версии Clojure реализованы следующие основные виды коллекций:

Кроме того, для вектора в Clojure создается функция одного аргумента (целого числа — индекса значения) с именем, совпадающим с именем символа, связанным с вектором. Это позволяет использовать имя вектора в качестве функции для доступа к нужному значению. Например, вызов (v 3) в данном коде:

Также как и для векторов, для отображений создается функция одного аргумента (ключа), которая позволяет использовать имя символа, связанного с отображением, для доступа к элементам. Например,

В Clojure также определены дополнительные виды отображений, позволяющие в специальных случаях добиться большей производительности:

«Ленивые» структуры данных

Как уже упоминалось выше, большая часть структур данных в Clojure (и функций для работы с этими структурами данных) являются «ленивыми» (lazy). В языке имеется явная поддержка «ленивых» структур данных, позволяя программисту эффективно работать с ними. Одним из достоинств поддержки «ленивых» структур данных является то, что можно реализовывать очень большие или бесконечные последовательности используя конечное количество памяти. «Ленивые» последовательности и функции также могут использоваться для обхода ограничений, связанных с отсутствием оптимизации хвостовых вызовов в Clojure.

В качестве примера использования «ленивых» структур данных давайте рассмотрим создание бесконечной последовательности чисел Фибоначи:

но поскольку мы работаем с «ленивыми» последовательностями, то значение будет создано мгновенно, без вычисления всех чисел Фибоначи. А само вычисление чисел будет происходить по мере надобности. Например, мы можем получить 55-е число Фибоначи с помощью следующего кода:

Переходные структуры данных (transients)

При интенсивной работе с неизменяемыми коллекциями иногда возникает слишком много промежуточных объектов, что достаточно неэффективно. В версии 1.1 появилась возможность временно использовать изменяемые коллекции данных используя переходные (transient) структуры данных. Эта функциональность была специально введена в язык для оптимизации производительности.

Основная идея заключается в том, чтобы избежать ненужного копирования данных, что происходит когда вы работаете с неизменяемыми данными. Стоит отметить, что не все структуры данных поддерживают эту возможность — в версии 1.1.0 поддерживаются векторы, отображения и множества, а списки — нет, поскольку для них нет существенного выигрыша в производительности. В общем виде работа с переходными структурами данных происходит следующим образом:

Рассмотрим простой пример использования стандартных и переходных структур данных 4 :

Как видно из этого примера, использование переходных структур дает достаточно большой выигрыш в производительности — примерно в четыре раза. А на некоторых структурах данных выигрыш в производительности может быть больше. Копирование исходных данных и создание неизменяемой структуры — это операции со сложностью O(1), при этом происходит эффективное использование оригинальных данных. Также стоит отметить, что использование переходных структур данных приводит к принудительной изоляции потока выполнения — изменяемые данные становятся недоступными из других потоков выполнения.

Более подробно о переходных структурах данных вы можете прочитать на сайте языка.

Базовые конструкции языка

Объявление и связывание символов

Имеется два вида связывания символа со значением:

Таким образом, говорится о двух областях видимости: лексической и динамической. Нужно иметь в виду, что это не то же самое, что и локальные и глобальные переменные. Хотя динамически связанные символы, как правило, глобальны, а лексически связанные — локальны. А как быть в случае конфликта лексической и динамической привязки?

Деструктуризация параметров

Существует две формы деструктуризации параметров — деструктуризация отображений, и деструктуризация векторов, строк и массивов.

Деструктуризация векторов, строк и массивов

Деструктуризация отображений

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

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

Кроме ключевых символов, в качестве ключей отображений вы можете использовать строки и символы. Для этого нужно использовать ключевое слово :strs — для строк и :syms — для символов, как это показано в следующих примерах:

Особенно полезна деструктуризация отображений для тех случаев, когда вы хотите иметь необязательные (и именованные) параметры в функциях. В этом случае, объявление функции будет иметь вид

Здесь мы определяем функцию, принимающую два обязательных параметра, и два необязательных, значения которых можно указывать после соответствующих ключевых символов.

Получение оригинальных значений

Деструктуризация вложенных структур

Также хочется отметить, что деструктуризация может применяться и к вложенным структурам, например, если у вас есть вектор векторов, или вложенные отображения. Например,

Но тут стоит быть осторожным, поскольку если вместо вектора вы передадите объект несовместимого типа, то вы получите ошибку, как во втором примере.

Дополнительные примеры вы можете найти на данной странице. Также хорошее описание деструктуризации параметров можно найти в книге «Clojure in Action».

Управляющие конструкции

Для управления потоком выполнения программы в Clojure имеется некоторое количество специальных форм, на основе которых затем строятся остальные примитивы языка, управляющие выполнением программы.

Условные операторы

Циклы и рекурсивные функции

Для организации циклов Clojure имеет несколько специальных форм, функций и макросов. Поскольку JVM имеет некоторые ограничения, не позволяющие реализовать оптимизацию хвостовых вызовов (Tail Call Optimization, TCO), то это накладывает ограничения на способ реализации некоторых алгоритмов, которые обычно реализуются через TCO в Scheme и других языках, поддерживающих эту оптимизацию.

Стоит также отметить, что достаточно часто рекурсивные функции можно преобразовать в функции, производящие «ленивые» последовательности, как это было показано в разделе «Ленивые» структуры данных. Это положительно сказывается на производительности кода и потреблении памяти.

Исключения

то на экран будет выведено следующее:

Функции

В Clojure имеется набор функций, которые позволяют создавать новые функции на основе существующих. Функция partial используется для создания функций с меньшим количеством аргументов путем подстановки части параметров (каррирование), а функция comp создает новую функцию из нескольких функций (композиция функций):

Макросы

Макросы — это мощное средство уменьшения сложности кода, позволяющие строить проблемно-ориентированную среду на основе базового языка. Макросы активно используются в Clojure, и множество конструкций, составляющих язык, определены как макросы на основе ограниченного количества специальных форм и функций, реализованных в ядре языка.

Макросы в Clojure смоделированы по образцу макросов в Common Lisp, и являются функциями, которые выполняются во время компиляции кода. В результате выполнения этих функций должен получиться код, который будет подставлен на место вызова макроса. Основная разница заключается в синтаксисе. В общем виде определение макроса выглядит следующим образом:

описание макроса (документация) — doc-string? и список атрибутов — attr-map? являются не обязательными, а список параметров и тело макроса могут указываться несколько раз, что позволяет определять макросы с переменным числом аргументов также, как и при объявлении функций (см. пример ниже).

Другим подходом является маскирование (quote) всего выражения, с раскрытием только нужных частей кода. Для этого используется специальный синтаксис записи ` (обратная кавычка), внутри которого можно использовать

Примеры макросов

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

Шаблоны

В версии 1.1 была введена поддержка шаблонов (templates), которые могут использоваться с следующих случаях:

Это выражение будет раскрыто в следующий код (вы можете проверить это с помощью macroexpand-1 ):

Мультиметоды

и попробуем применить ее к разным аргументам, то мы получим следующие результаты:

Но этот пример является достаточно простым и похож на стандартную диспетчеризацию в ОО-языках. Clojure предоставляет возможность диспетчеризации вызова в зависимости от значения аргументов, а также других признаков. Например, мы можем определить мультиметод с собственной функцией диспетчеризации, которая вызывает разные функции в зависимости от переданного значения:

и, вызывая этот мультиметод, мы получим соответствующие значения:

Протоколы и типы данных

Одно из самых больших изменений в Clojure версии 1.2 — введение в язык новых артефактов: протоколов (protocols) и типов данных (datatypes). Данные изменения позволяют улучшить производительность программ по сравнению с мультиметодами, что в будущем даст возможность написать Clojure на Clojure (в данный момент протоколы и типы данных уже активно используются при реализации Clojure).

Что это такое и зачем нужно?

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

Существует несколько причин введения протоколов и типов данных в новую версию языка:

В отличии от имеющегося в Clojure gen-interface (и соответствующих proxy/gen-class ) определение протоколов и типов не требует AOT (ahead-of-time) компиляции исходного кода, что упрощает распространение программ на Clojure. Однако при определении протокола, Clojure автоматически создает соответствующий интерфейс, который будет доступен для кода, написанного на Java.

Типы данных, определенные с помощью deftype или defrecord позволяют программисту на Clojure определять свои структуры данных, вместо использования обычных отображений и структур, но об этом ниже.

Важно помнить, что протоколы и типы данных с одним и тем же именем могут быть определены в разных пространствах имен, так что стоит быть осторожным и не наделать ошибок при импорте определений и последующей реализации протоколов!

Определение протоколов

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

название — единственный обязательный параметр, хотя определение протокола без функций не имеет особого смысла. В описании вы можете описать ваш протокол, и это описание будет показываться при вызове функции doc для вашего протокола. Для протокола вы можете указать одну или несколько сигнатур функций, где каждая сигнатура выглядит следующим образом:

Вы можете определять одну функцию, которая будет принимать различное количество параметров, но первым аргументом функции всегда является объект, на основании которого будет выполняться диспатчеризация, и к которому эта функция будет применяться. Вы можете рассматривать его как this в Java и C++. В дополнение к сигнатурам, вы можете описать вашу функцию, но это необязательно.

Давайте посмотрим на стандартный пример:

Данный протокол определяет две функции: bar — с двумя параметрами, и baz — с одним, двумя или тремя параметрами.

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

Реализация протоколов

Для этой функции вы указываете имя типа данных или класса (или nil ), и передаете список состоящий из названий протоколов ( протокол-1 и т.д.) и отображений, которые связывают функции протокола ( метод-1 и т.д.) с их реализациями — анонимными или именованными функциями.

Макрос extend-protocol использоваться в тех случаях, если вы хотите реализовать один протокол для нескольких типов данных или классов. В общем виде использование extend-protocol выглядит следующим образом:

При использовании, extend-protocol раскрывается в серию вызовов extend-type для каждого из используемых типов.

Давайте рассмотрим небольшой пример. Пусть мы объявим следующий простой протокол:

При использовании любой из этих реализаций для объекта класса String мы получим один и тот же ответ:

Определение типов данных

deftype и defrecord обычно имеют разные области применения: deftype в основном используется для «системных» вещей — коллекций, и т.п., тогда как defrecord в основном используется для хранения информации из «проблемной области» — данных о заказчиках, записях в БД и т.п. — то, для чего использовались отображения в версиях 1.0 и 1.1.

Давайте рассмотрим как использовать конкретные средства для создания типов данных.

deftype & defrecord

В общей форме использование макросов deftype и defrecord выглядит следующим образом:

Для обоих макросов обязательным параметром является лишь имя, которое становится именем класса. Поля, которые станут членами класса, перечисляются в векторе, следующем за именем, и могут содержать объявления типов. После этого вектора, можно указать список реализуемых интерфейсов и протоколов, вместе с реализацией (это не обязательно, поскольку для этого вы позже можете использовать extend-protocol & extend-type ).

Спецификации протоколов/интерфейсов выглядят следующим образом:

Вы можете указать любое количество протоколов/интерфейсов, которые будут реализованы данным типом данных. Давайте посмотрим на простейший тип данных, который реализует протокол Hello :

Мы можем вызвать функцию hello для нашего объекта, и получим следующий вывод:

Мы можем также создать тип с помощью defrecord :

и вызвать метод hello для этого типа:

то мы сможем изменять значение поля:

reify

Вот небольшой пример реализации протокола Hello для конкретного объекта:

И при вызове hello для этого объекта, мы получим соответствующий результат:

Дополнительные функции и макросы для работы с протоколами

Для работы с протоколами и типами данных определено некоторое количество вспомогательных функций, которые могут вам понадобиться:

extends? возвращает true если данный тип данных (2-й аргумент) реализует интерфейс, заданный первым аргументом; extenders возвращает коллекцию типов, реализующих заданный протокол; satisfies? возвращает true если данный протокол (1-й аргумент) применим к данному объекту (2-й аргумент);

Пространства имен и библиотеки

Наиболее часто используемыми функциями при работе с пространствами имен являются:

use помещает в текущее пространство имен символы (все, или только указанные) из другого пространства имен, в том числе и находящихся в других библиотеках, загружая их при необходимости; require загружает заданные библиотеки, но не помещает символы, определенные в них, в текущее пространство имен; import используется для библиотек JVM и импортирует заданные классы из указанного пакета.

Читайте также:  Грамматика английского языка быкова

Каждая из этих функций имеет разное количество параметров, описание которых можно найти в документации. В качестве пример давайте рассмотрим следующий код:

Описание дополнительных операций, которые можно производить с пространствами имен, вы можете найти в официальной документации.

Метаданные

Одним из интересных свойств Clojure является возможность связывания произвольных метаданных с символами, определенными в коде. Некоторые функции и макросы позволяют указывать определенные метаданные для управления видимостью символов, указания типов данных, документации, аннотаций для Java и т.п. Стоит отметить, что наличие метаданных никак не влияет на значения, связанные с символом. Например, если мы имеем два отображения, с одинаковым содержимым, но разными метаданными, то эти отображения будут эквивалентны между собой.

Некоторые специальные формы, такие как def и т.п., имеют определенный набор названий ключей метаданных, которые могут изменять поведение определяемого символа. Например, следующий код:

Разработчик имеет возможность считывания и изменения метаданных символов с помощью функций. Функция meta возвращает отображение, содержащее все имеющиеся метаданные. А с помощью функции with-meta можно добавить или изменить метаданные заданного символа.

Конкурентное программирование

Future & promise

Работа с изменяемыми данными

Стоит отметить, что в Clojure различаются понятия состояния (state) и «названия» (identity). Состояние — это значение, связанное с названием в конкретный момент времени. Значение, существуюшее в данном состоянии никогда не изменяется, а то, что выглядит обновлением данных, является на самом деле обновлением identity, которое начинает указывать на новое значение (state). В тоже время, старое состояние может продолжать существовать и использоваться из других потоков выполнения. Более подробно об этом можно прочитать в следующей статье или в 6-й главе книги Practical Clojure.

Имеющиеся средства для работы с изменяемыми данными можно классифицировать по нескольким параметрам, как показано в следующей таблице:

Вид изменения Синхронное Асинхронное
Координированное ref
Независимое atom agent
Изолированное var

Хорошим примером использования возможностей Clojure в части работы с изменяемыми данными в многопоточных программах является пример «муравьи» (ants) который Rich Hickey продемонстрировал в видеолекции «Clojure Concurrency в которой рассказывается о возможностях Clojure в части конкурентного программирования. Еще один хороший пример использования Clojure для таких задач можно найти в серии статей Tim Bray.

Ссылки (refs)

Синхронное изменение данных производится через ссылки на объекты данных. Изменение ссылок можно проводить только в рамках явно обозначенных транзакций. Изменение нескольких ссылок в рамках транзакции 15 является атомарной операцией, обеспечивающей целостность данных и выполняемой в изоляции (atomicity, consistency, isolation) — ACI (аналогично свойствам транзакций в базах данных, но без долговечности (durability)).

Изменение данных с помощью ссылок возможно благодаря использованию Software Transactional Memory, которая обеспечивает целостность данных при работе с ними из нескольких потоков выполнения. Описание принципов работы STM, вместе с подробным описанием ее реализации в Clojure, вы можете найти в статье Software Transactional Memory Марка Волкманна (R. Mark Volkmann).

Рассмотрим, например, код для управления набором счетчиков (например, для сбора статистики по каким-то действиям):

Загрузим этот код, выполним несколько функций и посмотрим на состояние переменной counters после выполнения каждой из функций:

Это простой пример, показывающий координированное изменение данных разных счетчиков, но эти функции можно использовать в разных потоках выполнения без страха потерять или получить неправильные данные.

Для обеспечения корректности данных, сохраняемых по ссылке, программист может установить функцию-валидатор. Это выполняется с помощью функции set-validator! (или сразу, при создании ссылки), которая получает два аргумента — ссылку и функцию-валидатор для данной ссылки. В том случае, если программист устанавливает некорректное значение, функция-валидатор должна вернуть ложное значение или выбросить исключение. Например, чтобы запретить отрицательные значения счетчиков, мы можем использовать следующую функцию-валидатор:

и если пользователь попробует установить отрицательное значение счетчика, то Clojure выдаст ошибку.

Более подробную информацию о работе с ссылками вы можете найти на сайте языка.

Агенты (agents)

Агенты позволяют осуществлять асинхронное обновление данных. Работа с агентами похожа на работу со ссылками (только вы должны использовать agent вместо ref ), но обновление данных может произойти в любой момент (и программист не может на это влиять) в зависимости от количества заданий. Эти задания выполняются в отдельном пуле потоков выполнения, размер которого ограничен. В отличие от ссылок, вам нет необходимости явно создавать транзакцию с помощью функции dosync — вы просто посылаете «сообщение», состоящее из функции, которая установит новое значение агента, и аргументов для этой функции.

Пример со счетчиками, переписанный на использование агентов, будет выглядеть следующим образом:

Разница между send и send-off заключается в том, что они используют разные по размеру пулы нитей выполнения. send рекомендуется применять для действий, которые ограничены по времени выполнения, такие как conj и т.д. А send-off лучше использовать для длительно выполняемых задач и задач, которые могут зависеть от ввода/вывода и других блокируемых операций.

В некоторых случаях вам может понадобиться, чтобы задания, посланные агенту, были завершены. Для этого в язык введены две функции, которые позволяют остановить выполнение текущего потока выполнения до завершения задач переданных агенту (или агентам). Функция await блокирует выполнение текущего кода до завершения всех задач, а функция await-for блокирует выполнение на заданное количество миллисекунд и возвращает контроль текущему потоку, даже если выполнение всех задач не было завершено.

Так же как и при использовании ссылок, при работе с агентами вы можете использовать функции-валидаторы и функции-наблюдатели. Остальную информацию об агентах вы можете найти на отдельной странице сайта языка.

Атомы (atoms)

Вот простой пример кода, который использует атомы:

При использовании данного кода мы можем быть уверены, что значение счетчика будет увеличиваться всегда, независимо от того, сколько потоков выполнения вызывают эту функцию. Подробней об атомах вы можете прочитать на сайте языка.

Взаимодействие с Java

Clojure реализует двухстороннее взаимодействие с библиотеками, работающими на базе JVM — код на Clojure может использовать существующие библиотеки и вызываться из других библиотек, реализовывать классы и т.п. Отдельно стоит отметить поддержку работы с массивами объектов Java — поскольку они не являются коллекциями, то Clojure имеет отдельные операции для работы с массивами: создание, работа с индивидуальными элементами, конвертация из коллекций в массивы и т.д. Подробную информацию о взаимодействии с Java вы можете найти на сайте языка.

Вы также можете встроить Clojure в ваш проект на Java и использовать его в качестве языка расширения. Дополнительную информацию об этом вы можете найти в учебнике о Clojure.

Работа с библиотеками Java

Для обращения к членам классов существует несколько форм:

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

создаст новое отображение и поместит в него два объекта.

применит метод toUpperCase из класса String к каждой из строк вектора. В простых случаях этот код можно заменить на анонимную функцию вида:

но в некоторых случаях memfn просто удобнее.

Вызов кода на Clojure из Java

Существует несколько причин, по которым вам может понадобится вызвать код, написанный на Clojure из Java. Первая причина — вам необходимо реализовать так называемые обратные вызовы (callbacks), которые будут реализовывать обработку каких-то событий, например, при обработке XML файла или при реализации GUI. Вторая причина — вы хотите реализовать некоторую функциональность на Clojure, и позволить классам Java пользоваться этой функциональностью.

Реализация обратных вызовов (callback) с помощью proxy

Например, если вы хотите обрабатывать XML с помощью парсера SAX, то вы можете создать свой класс, который будет обрабатывать определенные события:

и после выполнения этого кода на стандартный вывод будут выданы названия элементов, составляющих данный XML документ.

Создание классов с помощью gen-class

Поддержка языка

Эффективное использование языка невозможно без наличия инфраструктуры для работы с ним — редакторов кода, средств сборки, библиотек и т.п. вещей. Для Clojure имеется достаточное количество таких средств — как адаптированных утилит (Maven, Eclipse, Netbeans и т.п.), так и разработанных специально для этого языка — например, системы сборки кода Leiningen. Отладку приложений, написанных на Clojure, поддерживают почти все среды разработки, перечисленные ниже, а для профилирования можно использовать существующие средства для Java.

Среды разработки

В настоящее время для работы с Clojure разработано достаточно много средств — поддержка Clojure имеется в следующих редакторах и IDE:

Установка обоих пакетов может быть выполнена (и это рекомендуется авторами пакетов) через Emacs Lisp Package Archive. Небольшое описание того, как установить и настроить clojure-mode и SLIME, вы можете найти в записи в блоге Романа Захарова.

На домашней странице проекта вы можете найти необходимую информацию по установке плагина, а также скринкаст, демонстрирующий возможности VimClojure.

Информацию по установке вы можете найти на странице проекта.

Описание того, как установить эти средства можно найти на сайте разработчиков языка. Кроме того, процесс установки некоторых из этих средств можно найти в наборе скринкастов, созданных Sean Devlin.

Компиляция и сборка кода на Clojure

Сборку кода, написанного на Clojure, можно осуществлять разными способами — начиная с компиляции, используя Clojure в командной строке, и заканчивая использованием высокоуровневых утилит для сборки кода, таких как Maven и Leiningen.

В принципе, компиляция кода — необязательный этап, поскольку Clojure автоматически откомпилирует загружаемый код, и многие проекты пользуются этим, распространяясь в виде исходных кодов. Однако предварительная компиляция (ahead-of-time, AOT) позволяет ускорить загрузку вашего кода, сгенерировать код, который будет использоваться из Java, а также позволяет не предоставлять исходный код, что важно для коммерческих проектов.

Компиляция кода на Clojure осуществляется в соответствии со следующими принципами:

Компиляция кода с помощью Clojure

Провести компиляцию исходного текста можно и не запуская REPL, для этого можно воспользоваться следующей командой:

Чтобы не изобретать код для компиляции файлов Clojure для каждого нового проекта сборки, был создан проект clojure-ant-tasks, который определяет стандартные задачи (tasks) для компиляции и тестирования кода, написанного на Clojure. Подробное описание использования пакета задач вы можете найти на странице проекта.

Использование Maven с Clojure

Система сборки кода Maven достаточно популярна среди разработчиков на Java, поскольку она позволяет декларативно описывать процесс сборки, тестирования и деплоймента, а выполнение конкретных задач ложится на плечи конкретных модулей (plugins).

Для Maven написан модуль clojure-maven-plugin, который позволяет компилировать и тестировать код, написанный на Clojure. Этот модуль позволяет прозрачно интегрировать Clojure в существующую систему сборки на основе Maven. Кроме компиляции и тестирования, данный модуль определяет дополнительные задачи, такие как запуск собранного пакета, запуск REPL с загрузкой собранного пакета, а также запуск серверов SWANK или Nailgun, что позволяет использовать SLIME и VimClojure для интерактивной работы с собранным пакетом.

Подробное описание того, как использовать этот модуль вместе с Maven, вы можете найти в следующей статье.

Leiningen

Для Clojure также существует своя собственная система сборки — Leiningen, описывающая проекты и процесс сборки, используя язык Clojure. В последнее время эта система становится все более популярной — она имеет возможности расширения с помощью дополнительных модулей, например, для компиляции кода на Java и т.п.

Из коробки Leiningen позволяет выполнять базовые задачи — компиляцию кода, тестирование, упаковку кода в jar-архив, сборку jar-архива со всеми зависимостями и т.д. Кроме того, имеется базовая поддержка работы с Maven, что позволяет использовать собранный код в других проектах.

Репозитории кода

Некоторые системы сборки, такие как Maven и Leiningen, поддерживают автоматическую загрузку зависимостей из центральных репозиториев кода. Для Clojure также имеются отдельные репозитории, совместимые с этими системами.

Кроме того, для распространения библиотек написанных на Clojure, был создан проект clojars.org, который поддерживает работу с Maven и Leiningen, и на котором можно найти достаточно большое количество полезных библиотек.

Заключение

Я надеюсь, что данная статья помогла вам познакомиться с этим интересным языком. Количество проектов (в том числе и коммерческих) на Clojure постоянно увеличивается, и, может быть, вы также сможете использовать данный язык для написания программ, которые будут работать на платформе JVM.

2. В интернете можно найти примеры кода из книг On Lisp и Practical Common Lisp, переписанные на Clojure

3. Фактически, своего адреса в памяти

5. Специальные формы — это отдельные элементы языка, для которых не выполняются стандартные правила вычисления. Про специальные формы в Lisp вы можете прочитать в отдельной статье

6. Форма recur также может использоваться отдельно, без loop — тогда он выполнит переход к началу функции, в которой он используется

7. Результат должен быть объектом, унаследованным от Throwable

9. Стоит отметить, что можно добавлять только родительские объекты, а создание потомков возможно только через стандартные механизмы создания классов с помощью gen-class

10. Стоит однако отметить, что протоколы не реализуют monkey patching и внедрение методов (injection) в существующие типы данных.

11. Возможность реализации абстракций на Clojure и высокая скорость работы протоколов позволит в будущем написать Clojure на самой Clojure, без использования исходного кода на Java.

12. Люди знакомые с Haskell могут рассматривать протоколы как некоторое подобие типов классов (typeclasses) в этом языке, правда при этом нельзя определять реализации по умолчанию для методов.

13. Но extend может использоваться в тех случаях, когда вы хотите использовать одни и те же реализации для разных типов данных — в этом случае, вы можете создать отображение с нужными функциями, и использовать его для разных типов, например, как описано в следующем блог-постинге.

14. Стоит отметить, что изменяются не данные, а ссылки на данные. В статье мы будем говорить об «изменяемых данных», понимая под этим использование соответствующих механизмов изменения

15. Транзакции нужны не только для изменения данных, но и для координированного чтения данных из нескольких ссылок

16. Т.е. новое значение агента равно результату выполнения (apply функция состояние-агента аргументы)

17. В текущей версии Clojure количество попыток изменения ограничено значением 10000

18. Очень часто gen-class используется в объявлении пространства имен с помощью макроса ns

Источник

Поделиться с друзьями
Расскажем обо всем понемногу
Adblock
detector