Native records
Les formes de record d'OTP 29 au-delà des records ordinaires sous forme de tuples : leur représentation, leurs opérations et leurs erreurs. Les records tuples sont décrits dans termes.
OTP 29 classe les native records comme expérimentaux. Les faits ci-dessous
proviennent des sources épinglées de 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) et de
sondages en direct sur OTP 29.1.1.
Périmètre
Les trois formes au-delà des records tuples sont implémentées et partagent une même représentation :
| Forme | Syntaxe |
|---|---|
| Native record, local | -record #r{...}., #r{...}, X#r.f, X#r{...}, motifs, is_record/1,2,3 |
| Qualifié et importé | #m:r{...}, X#m:r.f, X#m:r{...}, motifs ; -export_record, -import_record |
| Inféré (anonyme) | X#_.f, X#_{...}, motif #_{...} |
Le module de réflexion records est fourni (réflexion). Non
fournis : term_to_binary des native records (error:notsup,
format externe des termes) et la mise à jour
de code à chaud.
Représentation
- Descripteur. Chaque définition de native record se compile en un
abi::v1::RecordDescriptorimmuable à côté du descripteur de son module : leModuleDescriptorqui le définit, des emplacements d'atomes pour les noms du module et du record, l'indicateur d'export (-export_recordà la compilation) et les emplacements d'atomes des noms de champs dans l'ordre de définition, ainsi que la liste des champs querecords:get_definition/2renvoie sous forme d'octets External Term Format ({Field, Default}avec la valeur par défaut réduite, ou simplementField). Les emplacements d'atomes sont résolus via les atomes enregistrés du module, comme les noms deFrameDescriptor. Les programmes sont statiques (pas de déchargement), donc un descripteur vit aussi longtemps que le programme ; un autre module du lot y fait référence par symbole. Les valeurs par défaut sont compilées dans chaque site de construction, jamais stockées. - Instance. Cellule du tas de type
native_record: en-tête (nombre1 + n), un mot de descripteur non tracé, puisnvaleurs tracées dans l'ordre de définition. Un record sans champ est une cellule de deux mots. - Le descripteur capture le module, le nom, l'indicateur d'export et la liste des champs à la construction, comme la définition capturée d'OTP. Toute opération après la construction lit le descripteur de l'instance, jamais le code qui l'exécute.
- La copie, le ramassage, le parcours et la vérification traitent la cellule comme un tuple dont le premier mot de charge utile n'est pas tracé. L'admission exige un descripteur enregistré par un module du même runtime.
Opérations
M est le module courant. Toutes les erreurs ont la classe error ; le
contenu de badrecord est la valeur fautive, sauf pour une construction externe
ratée.
| Opération | Accepte | Échec |
|---|---|---|
Construction locale #r{...} | Toujours (la définition est locale) | Champ inconnu ou valeur manquante sont des erreurs de compilation |
Construction externe #m:r{...} (aussi #r{...} importé, aussi #M:r) | m:r défini dans le lot et exporté | {badrecord, {m, r}} ; puis {badfield, {{m, r}, F}}, {novalue, {{m, r}, F}} (badfield l'emporte) |
Accès local X#r.f | Native record nommé r (module et indicateur d'export non vérifiés, comme le runtime d'OTP) | {badrecord, X} ; champ absent {badfield, {{Mod, r}, f}} |
Accès externe X#m:r.f | Module m, nom r, exporté | {badrecord, X} ; {badfield, ...} |
Accès anonyme X#_.f | Tout native record (export non vérifié, comme le runtime d'OTP) | {badrecord, X} ; {badfield, ...} |
Mise à jour locale X#r{...} | Module M, nom r | {badrecord, X} ; champ inconnu {badfield, ...} |
Mise à jour externe X#m:r{...} | Exporté, module m, nom r | idem |
Mise à jour anonyme X#_{...} | Exporté ou module M | idem |
Motif local #r{...} | Module M, nom r, chaque champ listé présent | pas de correspondance |
Motif externe #m:r{...} | Module m, nom r ; exporté quand un champ est listé | pas de correspondance |
Motif anonyme #_{...} | Tout native record ; exporté ou module M quand un champ est listé | pas de correspondance |
- La construction évalue les champs donnés dans l'ordre du source (les valeurs par défaut sont des littéraux) ; une mise à jour évalue d'abord le record, puis les nouvelles valeurs dans l'ordre du source, contrairement à une mise à jour de record tuple. Les mises à jour vides vérifient tout de même.
- Guards : seulement l'accès aux champs (un échec fait échouer la guard) et
is_record. Une construction dans une guard donnecreating a record in a guard is only supported for tuple records; une mise à jour donneillegal guard expression. is_record(X)n'est vrai que pour les native records.is_record(X, r)avec unrnatif local teste le moduleMet le nomr; avecrimporté dem, il teste le modulem.is_record(X, m, r)avec des atomes teste le module et le nom ; avec un entier, c'est le test de tuple.- Les native records ne sont pas des tuples :
is_tupleest faux ; les services d'élément et de taille les rejettent.
Règles à la compilation
Les messages suivent erl_lint :
- Les valeurs par défaut doivent être littérales après réduction des constantes
(nombres, atomes, chaînes,
[], cons/tuples/maps de ceux-ci, binaries composés uniquement de chaînes, et les opérateurs arithmétiques, de comparaison etnot/and/or/xorsur ces valeurs, réduits comme le fait le compilateur d'OTP) ;++,--,andalso,orelseet un opérateur qui échoue (1 / 0) sont illégaux :illegal default value for field a in native record r. - Construction locale sans valeur pour un champ sans valeur par défaut :
field a is not initialized in native record r; champ inconnu :field zz undefined in record r. Les champs inconnus dans les accès, mises à jour et motifs sont acceptés (OTP se contente d'avertir) ; ils échouent à l'exécution. - L'index
#r.a,record_info/2, les initialiseurs_ = Vet les raffinements de type de champ sont réservés aux records tuples, avec les messages d'OTP. - Un nom est soit un record tuple, soit un native record, soit un record
importé ; les conflits et les formes
-export_record/-import_recordincorrectes sont des erreurs.-export_recorddoit précéder les définitions de fonctions. - Les formes externes désignant un module hors du lot, un record non défini ou
non exporté se compilent et échouent à l'exécution comme dans OTP. La
construction externe est résolue par rapport au lot à la compilation : le
module importateur abaisse lui-même les valeurs par défaut littérales du
module qui le définit et référence son descripteur exporté ; les champs
inconnus lèvent
badfieldavant qu'une valeur manquante ne lèvenovalue(ErrorReason21).
Réflexion
records répond à partir de la définition capturée par un record :
get/2,get_module/1,get_name/1(badargpour une valeur qui n'est pas un record ou un champ absent),get_field_names/1,is_exported/1({badrecord, X}).create(Module, Name, Fields, #{is_exported => Boolean})construit une définition à l'exécution dans l'ordre des champs donné, internée par le serveur de code, de sorte que les créations égales la partagent ; un record compilé et un record créé ayant les mêmes module, nom, indicateur d'export et ordre des champs sont=:=. Erreurs dans l'ordre d'OTP :{badrecord, Name}pour les noms qui ne sont pas des atomes,{badmap, Options},badargpour les autres options, puis un passage sur les champs (badargpour un élément qui n'est pas une paire,{badfield, {{Module, Name}, Field}}pour un nom qui n'est pas un atome), puisbadargpour un champ répété.update(Src, Module, Name, Map)conserve la définition de Src :{badrecord, Src},{badrecord, Name}lorsque Module et Name ne correspondent pas,{badmap, Map}, puis{badfield, ...}dans l'ordre des clés.get_definition(Module, Name)décode la liste des champs du descripteur :{#{is_exported => Boolean}, Fields},badargpour un record qu'aucun module du programme ne définit.
Affichage et ordre
erlang:display/1affiche#m:r{a=1,b=2}: sans espaces, champs dans l'ordre de définition (OTP : ordre d'index de la table des atomes, différences).- Ordre des termes : tuple < native record < map. Les records se comparent par
module, nom, indicateur d'export (false d'abord), nombre de champs, noms des
champs dans l'ordre de définition, puis valeurs dans l'ordre de définition.
=:=exige en plus le même ordre des champs ;==compare les valeurs numériquement (le compilateur d'OTP réduit certains==sur des records à=:=, différences ; c'est la règle du runtime qui est suivie).
Différences conservées
L'ordre des champs à l'affichage, la réduction de ==, la construction
externe de modules non chargés et l'absence d'avertissements sur les records
sont consignés dans différences.
Clause