Введение в Clojure
для Java разработчиков

Как говориться, нам нужно то, что делает решение простых задач легким, а сложных - возможным.
Чаз Эмирик
«Программирование на Clojure»
Роман Махлин
2016г.
Agenda
- Факты о Clojure
- Success Stories
- Синтаксис
- Live demo
- Clojure пятью словами
- JVM
- Динамика
- Компиляция
- LISP
- Concurrency
- Java interop
- Платформы и сообщество
- Заключение
- Ссылки
- Q/A


Факты о Clojure
- Первый релиз в 2007 году
- Автор: Рич Хикки(Rich Hickey)
- В Top5 любимых языков по версии stackoverflow.com на 2015г.
- 17,500+ репозиториев на github.com




















и многие другие
Success stories

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

Герман Вейль
«Математический способ мышления»
Синтаксис
- Строки: "string"
- Символ: \c
- Логические значения: true false
- NULL: nil
- Ключевые слова: :keyword
- Числа: 1 2 3..., 0xff, 040, 12r78, 18N, 3/4
- Регулярные выражения: #"regex"
- Списки: '(1 2 3)
- Вектор: [1 2 3]
- Ассоциативный массив: {:key1 value1 :key2 value2}
- Множество: #{1 2 3}
- Функции: do, def, let, defn, fn, if, ., loop, recur, cond, try, throw, eval, quote, reduce, var, ...
(defn fast-pow [a n]
(cond (zero? n) 1
(even? n) (letfn [(square [x] (*' x x))]
(square (fast-pow a (/ n 2))))
:else (*' a (fast-pow a (dec' n)))))
public static double pow(double a, int b) {
double result = 1;
while(b > 0) {
if (b % 2 != 0) {
result *= a;
b--;
}
a *= a;
b /= 2;
}
return result;
}
Синтаксис
Java
Clojure

(ns conway.rules)
(defn gen-cell[](if (> (Math/random) 0.7) :alive :dead))
(defn seed-grid [rows cols]
(vec (take rows (repeatedly
(fn [] (vec (take cols (repeatedly gen-cell))))))))
(defn neighbors [[i j]]
(let [x ((juxt inc inc identity dec dec dec identity inc) i)
y ((juxt identity inc inc inc identity dec dec dec) j)]
(map vector x y)))
(defn count-neighbors [grid coord]
(let [n (map #(get-in grid %) (neighbors coord))]
(count (filter #(= % :alive) n))))
(defn sim-step [grid coord]
(let [n-live (count-neighbors grid coord)]
(if (= :alive (get-in grid coord))
(case n-live
(2 3) :alive
:dead)
(if (= 3 n-live) :alive :dead))))
(defn step [grid]
(into [] (for [i (range (count grid))]
(into [] (for [j (range (count (get grid i)))]
(sim-step grid [i j]))))))
Синтаксис
Игра жизнь

JVM



Clojure разработана для JVM
- Полная поддержка Java типов, кроме примитивов
- Все в Clojure является объектом
- Никаких врапперов - прямой доступ к Java
- Программа на Clojure компилируется в байт-код
- Можно вызывать Java из Clojure и даже наоборот
Динамический


Clojure - динамический язык

- REPL
- Read
- Eval
- Loop
- Полиморфизм функций
- Динамическая система типов
- Боксированные типы
Компилируемый



Clojure - компилируемый язык
Две основные фазы:
- Read
- Программа на clojure - валидные структуры данных на clojure
- Eval
- Доступны макросы
На выходе обычный jar фаил
LISP



- Компилятор доступен в Runtime
- Загрузка и изменение кода на лету
- Код на Clojure - валидная структура данных
- Маленькое ядро
- Макросы
- Функциональный
- Иммутабельный
- "Чистые функции"
- Функции высокого порядка
Clojure это LISP
CONCURRENCY


Clojure разработан для многопоточных приложений
- Персистентные структуры данных
- Транзакционная память
- Атомарные типы
- Агенты
Три вида переменных
- Vars - изолированны в рамках одного треда
- Refs - синхронные переменные между тредами, все сразу видят изменения
- Agent - Асинхронные изменения

(def bookshelf (ref #{}))
(defn shelve[book]
(dosync (alter bookshelf conj book)))
(defn unshelve [book]
(dosync (alter bookshelf disj book)))
CONCURRENCY
Пример:
- dosync - транзакция
- alter - аналог alter table из БД
Пример:
(def x (agent 0))
(defn increment [c n] (+ c n))
(send x increment 5) ; @x -> 5
(send x increment 10) ; @x -> 15
- agent - зранит состояние
- send - посылает значение

CONCURRENCY
(ns parallel-fetch
(:import (java.io InputStream InputStreamReader BufferedReader)
(java.net URL HttpURLConnection)))
(defn get-url [url]
(let [conn (.openConnection (URL. url))]
(.setRequestMethod conn "GET")
(.connect conn)
(with-open [stream (BufferedReader.
(InputStreamReader. (.getInputStream conn)))]
(.toString (reduce #(.append %1 %2)
(StringBuffer.) (line-seq stream))))))
(defn get-urls [urls]
(let [agents (doall (map #(agent %) urls))]
(doseq [agent agents] (send-off agent get-url))
(apply await-for 5000 agents)
(doall (map #(deref %) agents))))
(prn (get-urls '("http://lethain.com" "http://willarson.com")))


Java interop
- '.' для вызова метода объекта
- '.-' вызов геттера
- '/' для статического поля
- new для создания нового инстанса
Например:
(.toUpperCase "fred")
-> "FRED"
(.getName String)
-> "java.lang.String"
(.-x (java.awt.Point. 1 2))
-> 1
(System/getProperty "java.vm.version")
-> "1.6.0_07-b06-57"
Math/PI -> 3.141592653589793
Java interop
(bean java.awt.Color/black)
-> {:RGB -16777216, :alpha 255, :blue 0, :class java.awt.Color, :colorSpace #object[java.awt.color.ICC_ColorSpace 0x5cb42b "java.awt.color.ICC_ColorSpace@5cb42b"], :green 0, :red 0, :transparency 1}
- bean - превращает объект в мапу
- Можно опционально указать тип данных
(defn len [x] (.length x))
(defn len2 [^String x] (.length x))
user=> (time (reduce + (map len (repeat 1000000 "asdf"))))
"Elapsed time: 3007.198 msecs" 4000000
user=> (time (reduce + (map len2 (repeat 1000000 "asdf"))))
"Elapsed time: 308.045 msecs" 4000000

Java interop
- Отслеживание рефлексии
(set! *warn-on-reflection* true)
-> true
(defn foo [s] (.charAt s 1))
-> Reflection warning, line: 2 - call to charAt can't be resolved.
-> #user/foo
(defn foo [^String s] (.charAt s 1))
-> #user/foo
(defn hinted
(^String [])
(^Integer [a])
(^java.util.List [a & args]))
-> #user/hinted

Java interop
- Поддержка примитивов
-
функция с именем примитива возвращает этот примитив
-
функция с именем примитива во множественном числе возвращает массив примитивов
-
-
Огромный набор функций для поддержки операций с примитивами
static public float asum(float[] xs) {
float ret = 0;
for(int i = 0; i < xs.length; i++)
ret += xs[i];
return ret;
}
(defn asum [^floats xs]
(areduce xs i ret (float 0)
(+ ret (aget xs i))))
=>


Платформы и сообщество
- Typed Clojure - типизированная Clojure, с проверкой типов на этапе компиляции
- Clojurescript - Clojure в браузере - компилируется в javascript
- QUIL - Processing для Clojure
- Clojure CLR - Clojure для .NET
Каждый год проходит clojurecup - хакатон по этому языку коммандами до четырех человек со всего мира
Заключение

Clojure - функциональный и простой язык общего назначения, "новый LISP" под JVM, который позволяет делать все, что позволяет делать Java в функциональном стиле.
pros | cons |
---|---|
|
- Полная совместимость с Java, JVM
- Активно развивающийся язык, open source
- Динамика
- Лаконичность синтаксиса
- Параллейность "из коробки"
- Функциональность
- Clojurescript
- Огромная экосистема
- Наследуются все минусы JVM
- Довольно медленная процедура контрибьютинга
- Сложность отладки
- LISP многих пугает
Материалы

- clojure.org - домашняя страница
- lighttable.com - IDE
- cursive-ide.com - плагин для IDEA
- clojure for the brave and true - отличная книга
- clojure programming - еще одна отличная книга
- 4clojure.com - задачи по clojure
- clojure koans - коаны по clojure
- sicpdistilled.com - SICP для clojure
Q/A


Введение в Clojure для Java разработчиков
By Roman Makhlin
Введение в Clojure для Java разработчиков
- 1,200