Потокові макроси

Потокові макроси, також відомі як функції–стрілки, використовуються для запису вкладених викликів функцій у простішому форматі, що більш зрозумілий для читання.

Уявіть, що у вас є вираз (f (g (h x))), в якому функція f отримує результат обчислення функції g і так далі, з навіть більшою кількістю вкладених викликів. За допомогою потокового макросу -> такий вираз можна записати як (-> x (h) (g) (f)), що набагато легше читати.

Це синтаксичний цукор, бо функція–стрілка насправді є макросом, що перетворює вираз (-> x (h) (g) (f)) в (f (g (h x))) на етапі компілювання коду в JavaScript.

Прийміть до уваги, що круглі дужки навколо h, g та f — необов'язкові, якщо ці функції не мають додаткових параметрів. Тобто вираз (f (g (h x))) можна записати, як (-> x h g f).

Макрос першого потоку (->)

Його називають макросом першого потоку тому, що він встановлює значення першого аргументу для всіх виразів.

Нумо розглянемо наступний приклад без потокового макросу:

(def book {:name "Lady of the Lake"
           :readers 0})

(update (assoc book :age 1999) :readers inc)
;; => {:name "Lady of the lake" :age 1999 :readers 1}

З макросом -> це буде виглядати так:

(-> book
    (assoc :age 1999)
    (update :readers inc))
;; => {:name "Lady of the lake" :age 1999 :readers 1}

У ClojureScript функції, що трансформують структури даних завжди приймають дані першим аргументом. Тому цей потоковий макрос дуже зручний у використанні, коли треба записати підряд декілька таких трансформацій.

Макрос останнього потоку (->>)

Основна відмінність макросу останнього потоку від макросу першого потоку у тому, що макрос ->> встановлює значення останнім аргументом в усіх виразах.

Давайте розглянемо приклад:

(def numbers [1 2 3 4 5 6 7 8 9 0])

(take 2 (filter odd? (map inc numbers)))
;; => (3 5)

Той самий приклад з використанням макросу ->>:

(->> numbers
     (map inc)
     (filter odd?)
     (take 2))
;; => (3 5)

Цей потоковий макрос дуже зручний у використанні, коли треба трансформувати послідовності або колекції даних. Бо у ClojureScript функції, що трансформують послідовності та колекції, завжди приймають дані останнім аргументом.

Потоковий макрос (as->)

Інколи може бути так, що макроси першого та останнього потоку незручно використовувати. В таких випадках використовується макрос as->, що дозволяє встановити значення на довільне місце для кожного виразу окремо, а не лише в початок чи кінець виразу.

Цей макрос приймає два значення і довільну кількість виразів. Як і в звичайному потоковому макросі, перший аргумент — це значення, яке буде встановлено аргументом у всі вирази. А от другим аргументом повинен бути символ, з яким воно буде зв’язане. Цей символ використовується у всіх виразах в макросі для встановлення значення на довільне місце в аргументах функцій.

Давайте розглянемо приклад:

(as-> numbers $
  (map inc $)
  (filter odd? $)
  (first $)
  (hash-map :result $ :id 1))
;; => {:result 3 :id 1}

Потокові макроси some-> та some->>

Ще одна пара потокових макросів для спеціальних випадків. Вони працюють майже так само, як макроси -> та ->>, але мають можливість припинити виконання послідовності виразів, якщо один з них поверне nil.

Розглянемо такий приклад:

(some-> (rand-nth [1 nil])
        (inc))
;; => 2

(some-> (rand-nth [1 nil])
        (inc))
;; => nil

Це простий спосіб уникнення помилок, коли nil передається в функцію, яка цього не чекає.

Потокові макроси cond-> та cond->>

Макроси cond-> та cond->> також схожі на -> та ->> . Але вони дозволяють пропускати виконання виразів за допомогою умови, що записується перед кожним виразом.

Розглянемо приклад:

(defn describe-number
  [n]
  (cond-> []
    (odd? n) (conj "odd")
    (even? n) (conj "even")
    (zero? n) (conj "zero")
    (pos? n) (conj "positive")))

(describe-number 3)
;; => ["odd" "positive"]

(describe-number 4)
;; => ["even" "positive"]

Тут виконуються лише ті вирази, для яких умова обчислюється в логічне true.

Додаток

results matching ""

    No results matching ""