Стендап Сьогодні
📢
Канал в Telegram @stendap_sogodni
🦣
@stendap_sogodni@shevtsov.me в Федиверсі
27.05.2025
Функціональний стиль чи декларативний?
Зробив цікаву помилку. Це код власної перевірки для бібліотеки для тестування RSpec:
# expect(response).to be_foobar
RSpec::Matchers.define :be_foobar do |bar|
match do |actual|
check_foo(actual)
if bar
check_bar(actual, bar)
else
true
end
end
end
Помилка тут в тому, що якщо bar
немає, то перевірка завжди повертає успіх. Причина наче очевидна, якщо вже про неї знати: блок match
повинен повертати true
, якщо перевірка пройшла. Це звичайнісінька функція. Тому в мене перша перевірка фактично нічого не робить, бо її результат ніяк не враховується.
Мабуть, я це писав під впливом SwiftUI, де все не так:
VStack {
Text("Foo")
if bar != "" {
Text("Bar \(bar)")
}
}
Це вірний код: перший рядок буде видимим завжди, а другий — тільки якщо змінна не порожня. Власне, так саме працює й JSX та інші мови розмітки, але SwiftUI відрізняється тим, що це все нормальний код Swift.
Якби це був лісп — наприклад, Clojure - то там не тільки легко зробити макрос, який би виконував такий синтаксис та збирав результати, а й такого “дивного” більше, ніж традиційних функцій. Для простого прикладу наведу протяжні макроси (кложуристи, допоможіть, бо я трохи призабув):
(as-> result _
(check-foo _)
(and bar (check-bar _))
)
Але у Swift таких потужних макросів немає. Виявляється, заради SwiftUI придумали більш спеціалізований механізм - Result Builder. За наявністю анотації @resultBuilder
він збирає послідовність викликів у масив, а потім надсилає у відповідний інтерфейс. Ось так, не побоялися повністю перевизначати поведінку блоку коду без видимих на те ознак.