7. :as keyword
Иногда саму коллекцию тоже хочется иметь
в арсенале, тогда на помощь приходит :as
(defn [[head & tail :as coll]]
(doSomth ...))
8. destructuring everywhere
defn, fn и let - это там, где это доступно.
Но из-за того, что многое приводится
макросами к этим конструкциям,
destructuring доступен очень много где.
14. Getting all together
Наконец-то классический HelloWorld!
(.println (System/out)
"Hello, World!")
15. Operator .
Есть и альтернатива всему этому…
(.toUpperCase "abc")
(. "abc" toUpperCase)
(Thread/activeCount)
(. Thread activeCount)
(.equals "a" "b")
(. "a" equals "b")
16. Operator ..
Последовательные вызовы можно
объединить:
(. (. System out) println
"Hello, World!")
(.. System out
(println "Hello, World!"))
17. operator new
Есть старый добрый оператор:
(new ClassName args)
(new Object)
(new String "text")
(new java.util.HashMap)
18. dot syntax
Но есть и альтернатива…
(ClassName. args)
(Object.)
(String. "text")
(java.util.HashMap.)
19. for example
(let [h (java.util.HashMap.)]
(.put h "a" "b")
(.get h "a"))
20. import
Есть и возможность импортировать имена:
(import [java.util HashMap
ArrayList])
(HashMap.)
(ArrayList.)
21. operator doto
Что делает этот оператор?
(doto (HashMap.)
(.put 1 "one")
(.put 2 "two"))
31. :prefix
Можно добавить префикс для понятности:
:prefix prefix
Тогда реализовать:
(defn prefix-methodName
[arg] (str "x " arg))
32. :init/:constructors
Объявить имя для конструктора, и
сигнатуры конструкторов:
:init init
:constructors [[String] []
[String String] [String]]
33. :state
При создании класса можно записать
некоторый state, который можно вернуть
как [[super-call] state] из конструктора.
Вызывать можно так:
:state state
(.state this)
34. :implements/:extends
Указать супер класс:
:extends ClassName
Можно также объявить список интерфейсов:
:implements [java.io.Serializable]
35. :gen-class
Желательно все это использовать в макросе
ns, где и указать все, что требуется:
(ns my.namespace
(:gen-class))
44. derive
Иногда не хочеться писать несколько
реализаций одного и того же, тогда можно
использовать derive:
(derive ::square ::rectangle)
Теперь можно не писать реализацию area
для ::square.
45. :default
Также можно определить, что делать если
получили что-то не то (а не просто кидать
исключение):
(defmethod triangle-type :default [_]
:unknown)
48. defprotocol
По сути объявляем новый интерфейс (в
реальности подобный интерфейс и правда
появляется):
(defprotocol P
"doc string"
(method1 [a] "doc")
(method2 [a] [a b] "doc2")
59. deref
Вычисляет значение атома:
(deref my-atom)
Есть и альтернативный синтаксис, более
короткий:
@my-atom
60. swap!
Все изменения атомов должны быть без
side-effects, потому что они могут быть
вычислены несколько раз. Заменяет atom на
применение функции к аргументам.
(swap! my-atom fn args)
63. dosync
Этот метод запускает транзакцию.
Транзакция будет либо целиком выполнена,
либо целиком невыполнена. Может
вычисляться несколько раз.
(let [my-ref (ref false)]
(dosync (ref-set ref true))
67. commute
Исполняет функцию над значением ссылки
и присваивает ссылке.
Исполняется более конкурентно, чем alter,
поэтому требует от функции быть
коммутативной.
69. send
Позволяет отправить агенту задание на
изменение значения.
(def my-agent (agent 100))
(send my-agent + 100)
@my-agent
; 200
Агенты также интегрированы с STM. send в
транзакции действует как и alter.
70. future
Похоже на агента, только исполняется с
помощью java.util.concrrent.Future
(def ft (future
(reduce + (range 0 10000))))
(realized? ft) ;true
@ft ;49995000
71. locking
Позволяет удержать lock на каком-нибудь
объекте:
(let [a (java.util.ArrayList.)]
(locking a
(.add a 10)))
Помогает в тех случаях, когда нужна
совместимость с Java кодом.
72. bank account homework
Давайте напишем transfer денег с одного
банковского аккаунта на другой.
74. macros
Позволяет исполнить код на этапе
компиляции. Макро-функции могут
изменить дерево программы, тем самым, мы
можем изменять синтаксис языка.
(defmacro call [sym & params]
(cons sym params))
(call + 1 2 3)
75. unquote
Можно новое дерево не составлять вручную,
как в предыдущем примере, а собрать с
помощью unquote:
(defmacro twice
[form]
`(do
~form
~form))
77. gensym
Иногда в макросе хотим создать новую
переменную, но не всегда мы хотим дать ей
разумное имя, которое может пересекаться с
чем-то еще:
(defmacro f
[t]
`(let [z# ~t]))
78. unquote symbol
Можем задать новый символ, который будет
виден магическим образом в коде
(defmacro with-a
[& body]
`(let [~'a 2]
~@body))
(with-a (println a))
79. macroexpand
Метод, с которым можно дебажить макросы.
(defmacro with-a
[& body]
`(println ~body))
(macroexpand '(with-a 123))
; (clojure.core/println (123))
80. macroexpand-1
Предыдущий метод повторяет этот, пока это
имеет смысл, а этот метод раскрывает
макрос той формы, которую ему передали,
но не раскрывает подформы.
Для этого есть macroexpand-all.
81. or homework
Давайте напишем свою реализацию or:
(defmacro my-or
([] nil)
([x] x)
([x & next]
...))
82. let homework
А теперь let, через анонимную функцию:
(defmacro my-let
[bindings & body]
...
)