Guard
Допустимість guard дотримується зафіксованих правил erl_internal/erl_lint
з OTP 29; вона ніколи не виводиться з реєстрації в runtime. Перевіряється кожен
операнд, зокрема недосяжні. Guard успішний лише тоді, коли кінцеве значення —
атом true.
Потік керування
,об'єднує перевірки по порядку.;починає нову альтернативу післяfalse, небулевого значення або досягнутої семантичної помилки; коли всі альтернативи невдалі, клауза не збігається.- Зв'язування з голови доступні для читання в кожній альтернативі; guard не створюють зв'язувань.
- Інфраструктурні помилки (OOM, обмеження, володіння) зупиняють виконання і ніколи не переходять до іншої альтернативи.
| Оператор | Семантика |
|---|---|
A andalso B | A має бути булевим; false → false; інакше повертає B як будь-який терм |
A orelse B | A має бути булевим; true → true; інакше повертає B |
and, or, xor | Обидва операнди обчислюються по порядку, обидва булеві |
not A | Булеве заперечення |
true andalso 7 повертає 7 у тілі і є невдалою як кінцева перевірка guard;
(true andalso 7) =:= 7 успішна. Досягнута помилка всередині вкладеного виразу
відхиляє всю альтернативу: hd([]) orelse true невдала, hd([]); true
успішна. У тілах неправильні строгі операнди викликають badarg, а
неправильний лівий операнд лінивого оператора викликає {badarg, Value}, як в
OTP.
Каталог
guards.tsv перелічує всі 81
сигнатуру guard із зафіксованих таблиць guard_bif, new_type_test,
old_type_test, arith_op, bool_op і comp_op, а тест вимагає точної
рівності множин з upstream. Маніфест аудиту
зіставляє кожну з власником у розв'язувачі, пониженні та runtime. Усі 81
реалізовано (залежні від ідентичності self/0, node/0,1 і нативний
is_record/1 — починаючи з step 52 плану): self() — pid процесу, що викликає,
node() і node/1 для pid, посилання або порту дорівнюють nonode@nohost
(вузол лише один; будь-який інший аргумент node/1 робить guard невдалим, а в
тілі дає badarg), а is_record/1 перевіряє, чи це native record.
is_record/2 з назвою локального native record перевіряє модуль і назву;
is_record/3 з атомом як третім аргументом перевіряє модуль і назву native
record; доступ до нативного поля робить guard невдалим за будь-якої
невідповідності.
- Перевірки типів:
is_atom,is_integer,is_float,is_number,is_boolean,is_tuple,is_list,is_map,is_binary,is_bitstring,is_function/1,2(fun),is_pid,is_reference(pid і посилання);is_portзавжди false (портів не існує). is_integer(V, Lo, Hi)спершу перевіряє, що обидві межі є цілими числами (інакшеbadarg), потім перевіряє належність до діапазону включно з межами; переставлені межі дають false.- Порівняння
==,/=,=:=,=/=,<,=<,>,>=; арифметичні та побітові оператори; запити й перетворення відповідно до термів. - Конструктори та оновлення map у guard використовують ті самі кореневі сервіси, що й тіла.
- Перевірки record: див. терми.
Розв'язання
- Некваліфіковані guard BIF враховують локальні визначення, імпорти та
глобальний/вибірковий
no_auto_import.erlang:F(...)іerlang:'op'(...)оминають затінення, але лише для guard BIF і операторів. - Застарілі перевірки (
integer/1,float/1,atom/1,record/2, ...) допустимі лише на верхньому рівні перевірки guard (дужки дозволені). Тамfloat(X)означаєis_float; вкладений або кваліфікованийfloat/1є перетворенням. Застарілийrecord/2ігнорує приглушення своєї старої назви, але локальнийis_record/2його блокує. - У guard заборонені: присвоєння, довільні локальні/віддалені/динамічні виклики,
++,--,!, fun, comprehension, потік керування, оновлення record,record_info/2.
Обмеження оракула
Встановлений OTP 29.1.1 аварійно завершується під час перетворення на SSA для деяких сторонніх сучасних імпортів за застарілими скалярними псевдонімами; Clause явно відхиляє такого неавторизованого власника. Завантажувач OTP також відхиляє guard з величезною буквальною арністю record, хоча lint його приймає; для цього випадку є лише семантичні свідчення.
Clause