Трединг-макросы -> и ->>
Учимся записывать цепочки преобразований данных линейно, а не вложенными скобками.
Трединг-макрос (threading macro) — конструкция, которая «протягивает» значение через цепочку функций, делая код читаемым сверху вниз.
Проблема глубокой вложенности
Вспомним пример из прошлого урока: (reduce + (map sq (filter even? coll))). Читать его нужно изнутри наружу — сначала filter, потом map, потом reduce. Чем длиннее цепочка, тем труднее. Трединг-макросы переворачивают запись в естественный порядок.
Макрос -> (thread-first)
Макрос -> берёт первое значение и подставляет его первым аргументом в следующий вызов, затем результат — первым аргументом в следующий, и так далее.
; без макроса
(assoc (assoc {} :a 1) :b 2)
; с -> читается сверху вниз
(-> {}
(assoc :a 1)
(assoc :b 2))
; => {:a 1, :b 2}Вывод:
{:a 1, :b 2}Здесь {} подставляется первым аргументом в (assoc :a 1), давая (assoc {} :a 1), затем результат — в следующий assoc.
Макрос ->> (thread-last)
Макрос ->> подставляет значение последним аргументом. Это идеально для map, filter, reduce, у которых коллекция стоит в конце.
(->> [1 2 3 4 5 6]
(filter even?)
(map #(* % %))
(reduce +))
; чётные -> квадраты -> сумма => 56Вывод:
56
Тот же расчёт, что в прошлом уроке, но теперь читается как пайплайн: «взять вектор → отфильтровать чётные → возвести в квадрат → сложить».
Когда какой
| Макрос | Куда подставляет | Для чего |
-> | первым аргументом | работа с map: assoc, update, get |
->> | последним аргументом | работа с коллекциями: map, filter, reduce |
Как работает под капотом
Это макросы (а не функции): они работают с кодом как с данными ещё до выполнения. На этапе компиляции -> переписывает цепочку обратно во вложенные вызовы. То есть (-> x (f a) (g b)) разворачивается в (g (f x a) b). В рантайме никакой дополнительной работы нет — это чисто синтаксическое преобразование, и поэтому threading ничего не стоит по скорости. Здесь напрямую видна польза гомоиконности: макрос меняет структуру кода.
Частые ошибки
- Перепутать
->и->>. Для коллекций (map/filter) нужен->>, для map-структур (assoc/update) —->. - Threading там, где один вызов. Для единственной операции макрос лишний и только запутывает.
- Смешивать порядки аргументов. Если в цепочке функции с разным положением аргумента, бывает нужен
as->(более гибкий вариант).
Итоги
- Трединг-макросы превращают вложенные вызовы в линейный пайплайн.
->подставляет значение первым аргументом (для map-структур).->>подставляет последним аргументом (для коллекций: map/filter/reduce).- Это синтаксическое преобразование на этапе компиляции — бесплатно по скорости.