Стендап Сьогодні
📢
Канал в Telegram @stendap_sogodni
🦣
@stendap_sogodni@shevtsov.me в Федиверсі
15.06.2025
Метапрограмування та особливий стиль Clojure
В Clojure, абсолютно впевнено, код є даними. Насамперед для цього потрібно, щоб код не відрізнявся від даних на вигляд: саме для цього в ліспах такий дивакуватий синтаксис, де код не має особливих відзнак. Будь-яка конструкція з дужок може бути як “нормальним кодом”, так і вхідними даними для макроса, який інтерпретує цю конструкцію аж будь-яким чином.
Є макроси ситуативні: defroutes
чи html5
тощо. Вони нагадують схожі конструкції в Ruby чи JavaScript, бо принаймні не ставлять задачі вигадати нові мовні конструкції. Але то тільки верхівка макросів на Clojure… Бо справжній айсберг то макроси, що спрощують деяку сталу мовну конструкцію. Таких повно в самій стандартній бібліотеці. Так званих спеціальних форм, тобто дійсно “аксіоматичного” коду, який напряму розуміється компілятором, лише з десяток. А всі інші — як cond, аналог switch-case
, вже реалізовані як макроси.
Але звісно, стандартною бібліотекою все тільки починається. Бо все можна переробити під себе, або принаймні знайти вже перероблене.
Як приклад: бібліотека swiss-arrows. В Clojure вже є доволі проста протяжна форма (-> x f g)
, що еквівалентно (g (f x))
. (Це лише один спосіб спростити вкладені виклики.) Але ця бібліотека додає макроси для набагато складніших ситуацій, як-от діамантова вудка (sic) -<><
, що виконує одну форму та поширює результат до списку інших, можливо й в паралелі: (-<>< (+ 1 2) [<> 3] [4 <>])
.
Не знаючи макроса, під час просто неможливо зрозуміти, що він робить. (Бо ота вудка нічим не видає своєї дії.) Такі можливості створюють екосистему, де від проєкту до проєкту код може відрізнятися радикально. Це й гарно і погано водночас.
Гарно, бо можна ліпити код під себе, та майже нічого не стоятиме поперек шляху. Та повір, знаходити (чи створювати) та використовувати зручні макроси — на Clojure частина повсякденної інженерії, та без неї виходитиме багатослівно та теж незрозуміло.
Погано, бо немає ніякого стандарту. Ідіоми Clojure містять більш загальні твердження, як-от “пиши функціональний код” та “використовуй функції вищого порядку.” Коли шукаєш приклади чи читаєш код бібліотек, часто здається, що вони написані різними мовами (та певною мірою, так і є!)
Чимсь все це нагадує бібліотеку Lodash, бо там теж можна писати фільтри та інші параметри різними спрощеними шляхами: _.map(xs, ["foo" bar])
. Але я б радив спробувати Clojure, щоб на собі відчути цю силу вигадувати код таким, яким це зручно для твого проєкту.