Native records
Форми record з OTP 29, що виходять за межі звичайних кортежних record: їхнє подання, операції та помилки. Кортежні record описано в термах.
OTP 29 позначає native records як експериментальні. Наведені нижче факти взято
із зафіксованих вихідних кодів maint-29 (erl_lint, erl_expand_records,
v3_core, beam_core_to_ssa, erts/emulator/beam/erl_record.c, utils.c,
erl_printf_term.c) і живих перевірок на OTP 29.1.1.
Обсяг
Усі три форми, що виходять за межі кортежних record, реалізовано, і вони мають спільне подання:
| Форма | Синтаксис |
|---|---|
| Native record, локальний | -record #r{...}., #r{...}, X#r.f, X#r{...}, зразки, is_record/1,2,3 |
| Кваліфікований та імпортований | #m:r{...}, X#m:r.f, X#m:r{...}, зразки; -export_record, -import_record |
| Виведений (анонімний) | X#_.f, X#_{...}, зразок #_{...} |
Модуль рефлексії records надається (рефлексія). Не надаються:
term_to_binary для native records (error:notsup,
зовнішній формат термів) і гаряче оновлення
коду.
Подання
- Дескриптор. Кожне визначення native record компілюється в один незмінний
abi::v1::RecordDescriptorпоруч із дескриптором свого модуля: визначальнийModuleDescriptor, слоти атомів для назв модуля і record, прапорець експорту (-export_recordпід час компіляції) і слоти атомів назв полів у порядку визначення, а також список полів, якийrecords:get_definition/2повертає як байти External Term Format ({Field, Default}зі згорнутим значенням за замовчуванням або простоField). Слоти атомів розв'язуються через зареєстровані атоми модуля, як і назвиFrameDescriptor. Програми статичні (без вивантаження), тож дескриптор живе стільки ж, скільки програма; інший модуль пакета посилається на нього за символом. Значення за замовчуванням компілюються в кожне місце конструювання і ніколи не зберігаються. - Екземпляр. Комірка купи виду
native_record: заголовок (лічильник1 + n), невідстежуване слово дескриптора, потімnвідстежуваних значень у порядку визначення. Record без полів — комірка з двох слів. - Дескриптор фіксує модуль, назву, прапорець експорту і список полів під час конструювання, як і захоплене визначення в OTP. Кожна операція після конструювання читає дескриптор екземпляра, а не код, який її виконує.
- Копіювання, збирання сміття, обхід і перевірка трактують комірку як кортеж, перше слово корисного навантаження якого не відстежується. Допуск вимагає дескриптора, зареєстрованого модулем того самого runtime.
Операції
M — поточний модуль. Усі помилки мають клас error; вмістом badrecord є
значення, що спричинило помилку, за винятком невдалого зовнішнього
конструювання.
| Операція | Приймає | Невдача |
|---|---|---|
Локальне конструювання #r{...} | Завжди (визначення локальне) | Невідоме поле або відсутнє значення — помилки компіляції |
Зовнішнє конструювання #m:r{...} (також імпортований #r{...}, також #M:r) | m:r визначено в пакеті й експортовано | {badrecord, {m, r}}; потім {badfield, {{m, r}, F}}, {novalue, {{m, r}, F}} (badfield має перевагу) |
Локальний доступ X#r.f | Native record з назвою r (модуль і прапорець експорту не перевіряються, як у runtime OTP) | {badrecord, X}; поле відсутнє — {badfield, {{Mod, r}, f}} |
Зовнішній доступ X#m:r.f | Модуль m, назва r, експортований | {badrecord, X}; {badfield, ...} |
Анонімний доступ X#_.f | Будь-який native record (експорт не перевіряється, як у runtime OTP) | {badrecord, X}; {badfield, ...} |
Локальне оновлення X#r{...} | Модуль M, назва r | {badrecord, X}; невідоме поле — {badfield, ...} |
Зовнішнє оновлення X#m:r{...} | Експортований, модуль m, назва r | так само |
Анонімне оновлення X#_{...} | Експортований або модуль M | так само |
Локальний зразок #r{...} | Модуль M, назва r, кожне перелічене поле присутнє | немає збігу |
Зовнішній зразок #m:r{...} | Модуль m, назва r; експортований, якщо перелічено поле | немає збігу |
Анонімний зразок #_{...} | Будь-який native record; експортований або модуль M, якщо перелічено поле | немає збігу |
- Конструювання обчислює задані поля в порядку вихідного коду (значення за замовчуванням — літерали); оновлення спочатку обчислює record, потім нові значення в порядку вихідного коду, на відміну від оновлення кортежного record. Порожні оновлення все одно виконують перевірку.
- Guard: лише доступ до поля (невдача робить guard невдалим) і
is_record. Конструювання в guard даєcreating a record in a guard is only supported for tuple records; оновлення —illegal guard expression. is_record(X)істинне лише для native records.is_record(X, r)з локальним нативнимrперевіряє модульMі назвуr; зr, імпортованим зm, — модульm.is_record(X, m, r)з атомами перевіряє модуль і назву; з цілим числом це перевірка кортежу.- Native records не є кортежами:
is_tupleдає false; сервіси елементів і розміру їх відхиляють.
Правила часу компіляції
Повідомлення дотримуються erl_lint:
- Значення за замовчуванням мають бути літералами після згортання констант
(числа, атоми, рядки,
[], cons-комірки/кортежі/map із них, binary лише з рядків, а також арифметичні оператори, оператори порівняння таnot/and/or/xorнад ними, згорнуті так, як їх згортає компілятор OTP);++,--,andalso,orelseта оператор, що завершується помилкою (1 / 0), недопустимі:illegal default value for field a in native record r. - Локальне конструювання без значення для поля без значення за замовчуванням:
field a is not initialized in native record r; невідоме поле:field zz undefined in record r. Невідомі поля в доступі, оновленні та зразках приймаються (OTP лише попереджає); вони завершуються помилкою під час виконання. - Індекс
#r.a,record_info/2, ініціалізатори_ = Vі типізовані уточнення полів доступні лише для кортежних record, з повідомленнями OTP. - Назва належить або кортежному record, або native record, або імпортованому
record; конфлікти та неправильні форми
-export_record/-import_recordє помилками.-export_recordмає передувати визначенням функцій. - Зовнішні форми, що називають модуль поза пакетом, невизначений або
неекспортований record, компілюються і завершуються помилкою під час
виконання, як в OTP. Зовнішнє конструювання розв'язується відносно пакета під
час компіляції: імпортувальний модуль сам понижує літеральні значення за
замовчуванням визначального модуля і посилається на його експортований
дескриптор; невідомі поля викликають
badfieldраніше, ніж відсутнє значення викличеnovalue(ErrorReason21).
Рефлексія
records відповідає на основі визначення, захопленого record:
get/2,get_module/1,get_name/1(badargдля значення, що не є record, або відсутнього поля),get_field_names/1,is_exported/1({badrecord, X}).create(Module, Name, Fields, #{is_exported => Boolean})будує визначення під час виконання в заданому порядку полів; сервер коду інтернує його, тож однакові створення використовують спільне визначення; скомпільований record і створений record з однаковими модулем, назвою, прапорцем експорту та порядком полів є=:=. Помилки в порядку OTP:{badrecord, Name}для назв, що не є атомами,{badmap, Options},badargдля інших параметрів, потім один прохід по полях (badargдля елемента, що не є парою,{badfield, {{Module, Name}, Field}}для назви, що не є атомом), потімbadargдля повторного поля.update(Src, Module, Name, Map)зберігає визначення Src:{badrecord, Src},{badrecord, Name}, коли Module і Name не збігаються,{badmap, Map}, потім{badfield, ...}у порядку ключів.get_definition(Module, Name)декодує список полів дескриптора:{#{is_exported => Boolean}, Fields},badargдля record, якого не визначає жоден модуль програми.
Друк і порядок
erlang:display/1друкує#m:r{a=1,b=2}: без пробілів, поля в порядку визначення (в OTP — у порядку індексів таблиці атомів, відмінності).- Порядок термів: кортеж < native record < map. Record порівнюються за модулем,
назвою, прапорцем експорту (false спочатку), кількістю полів, назвами полів у
порядку визначення, потім значеннями в порядку визначення.
=:=додатково вимагає однакового порядку полів;==порівнює значення чисельно (компілятор OTP згортає деякі==над record до=:=, відмінності; дотримуємося правила runtime).
Збережені відмінності
Порядок полів під час виведення, згортання ==, зовнішнє конструювання
незавантажених модулів і відсутні попередження щодо record описано у
відмінностях.
Clause