24.9.11

Что нового в Clojure 1.3?

Сегодня ночью была выпущена новая версия языка Clojure — 1.3. Что нового в этой версии? Давайте рассмотрим основные изменения сделанные в этой версии (детальный список изменений можно найти в соответствующей ветке репозитория).

Расширенная поддержка примитивных типов

В предыдущих версиях, примитивные типы (long, double, etc.) поддерживались только для локальных арифметических операций и при объявлении полей deftype/defrecord, но не для аргументов функций и не для возвращаемых значений. Boxing выполнялся всегда, что часто приводило к ощутимым накладным расходам.

В новой версии примитивные типы поддерживаются в полной мере, что приводит к значительному увеличению производительности кода (в соответствующих тестах, в среднем в 3-5 раз). Соответственно изменилась работа с арифметическими выражениями — для примитивных типов будут использоваться стандартные операторы Java. Но при этом не будет происходит автоматического превращения в "Big*" типы при переполнении, как это происходило раньше — вместо этого будет брошено исключение. Если вам все-таки нужно автоматическая конвертация, то вы можете использовать операторы +', *', и т.д., хотя они и более медленные. Битовые операции теперь только поддерживаются для примитивных типов, и вся их поддержка для "Big*" была убрана из языка.

Для использования примитивных типов вам необходимо добавить соответствующие type hints, например:

(defn fib ^long [^long n]
    (if (<= n 1)
        1
        (+ (fib (dec n)) (fib (- n 2)))))

Более подробно обо всем этом на соответствующей странице JIRA.

Улучшения defrecord и deftype

В версии 1.3 были сделаны различные улучшения для работы с defrecord и deftype:

  • новый синтаксис для чтения и записи — либо с метками (#myns.MyRecord{:a 1, :b 2} — только для записей), либо позиционный (#myns.MyRecord[1 2] — для записей и типов);
  • автоматическая генерация функций для создания типов и записей: myns/map->MyRecord (только для записей) и myns/->MyRecord (для записей и типов).

Подробнее можно прочитать вот тут.

Reflection API

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

Модульный clojure-contrib

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

Множественные улучшения производительности

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

  • Более быстрый доступ к переменным;
  • Ускорение загрузки
  • Promises не используют блокировок (locks);
  • Инлайнинг nil?, битовых функций, математических операций, функций min & max с разным числом аргументов;
  • и многое другое...

Прочее

Помимо новой функциональности описанной выше, а также различных исправлений ошибок, в новой версии реализованы новые вещи (перечислены не все):

  • Некоторые вспомогательные пространства имен (clojure.set, clojure.xml, clojure.zip) не загружаются при запуске;
  • Объявления сделанные с помощью def более не являются "динамическими" по умолчанию — вы должны использовать ^:dynamic если хотите иметь возможность использования ее в binding;
  • Улучшенные сообщения об ошибках и исключениях;
  • Можно вызвать deref с указанием таймаута;
  • Функция comp возвращает identity если вызвана без параметров;
  • Возможность указания разных возвращаемых типов в объявлениях одной функции с разными количествами аргументов;
  • Некоторые функции перемещены в другие пространства имен;

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

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

Скажите, а в Clojure всегда так относятся к обратной совместимости или только в этом релизе? Ну или может где-то на сайте проекта есть какие-то оговорки, ну например что до версии 2.0 мы гарантируем никакой обратной совместимости?

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

ну большая часть кода должна работать без изменений, самое большое что сломали - clojure-contrib, но это было запланировано

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

Отличный обзор! Спасибо!

13-49 комментирует...

Что-то с автоматическим расширением интегральных типов какая-то ерунда. Common Lisp вполне себе автоматически преобразует fixnum в bignum и обратно по результату операции. А внутри ещё и fixnum в unboxed машинный вид перегоняет для скорости.

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

я так понимаю, что оверхед слишком большной на JVM :-(