Records nativos
Las formas de record de OTP 29 más allá de los records ordinarios de tupla: su representación, operaciones y errores. Los records de tupla están en términos.
OTP 29 marca los records nativos como experimentales. Los datos siguientes
provienen de los fuentes fijados 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) y de pruebas
en vivo sobre OTP 29.1.1.
Alcance
Las tres formas que van más allá de los records de tupla están implementadas y comparten una única representación:
| Forma | Sintaxis |
|---|---|
| Record nativo, local | -record #r{...}., #r{...}, X#r.f, X#r{...}, patrones, is_record/1,2,3 |
| Cualificado e importado | #m:r{...}, X#m:r.f, X#m:r{...}, patrones; -export_record, -import_record |
| Inferido (anónimo) | X#_.f, X#_{...}, patrón #_{...} |
El módulo de reflexión records está disponible (reflexión).
No disponibles: term_to_binary de records nativos (error:notsup,
formato externo de términos) y la
actualización de código en caliente.
Representación
- Descriptor. Cada definición de record nativo se compila a un único
abi::v1::RecordDescriptorinmutable junto al descriptor de su módulo: elModuleDescriptorque lo define, slots de átomo para los nombres del módulo y del record, el indicador de exportación (-export_recorden tiempo de compilación) y los slots de átomo de los nombres de campo en orden de definición, y la lista de campos que devuelverecords:get_definition/2como bytes del formato externo de términos ({Field, Default}con el valor por defecto plegado, o soloField). Los slots de átomo se resuelven mediante los átomos registrados del módulo, como los nombres deFrameDescriptor. Los programas son estáticos (sin descarga), por lo que un descriptor vive tanto como el programa; otro módulo del lote se refiere a él por símbolo. Los valores por defecto se compilan en cada punto de construcción y nunca se almacenan. - Instancia. Celda del heap de tipo
native_record: cabecera (recuento1 + n), una palabra de descriptor no rastreada y despuésnvalores rastreados en orden de definición. Un record sin campos es una celda de dos palabras. - El descriptor captura módulo, nombre, indicador de exportación y lista de campos en la construcción, como hace la definición capturada de OTP. Toda operación posterior a la construcción lee el descriptor de la instancia, nunca el código que la ejecuta.
- La copia, la recolección, el recorrido y la verificación tratan la celda como una tupla cuya primera palabra de contenido no se rastrea. La admisión exige un descriptor registrado por un módulo del mismo runtime.
Operaciones
M es el módulo actual. Todos los errores tienen la clase error; el contenido
de badrecord es el valor causante, salvo en la construcción externa fallida.
| Operación | Acepta | Fallo |
|---|---|---|
Construcción local #r{...} | Siempre (la definición es local) | Un campo desconocido o un valor ausente son errores de compilación |
Construcción externa #m:r{...} (también #r{...} importado, también #M:r) | m:r definido en el lote y exportado | {badrecord, {m, r}}; después {badfield, {{m, r}, F}}, {novalue, {{m, r}, F}} (prevalece badfield) |
Acceso local X#r.f | Record nativo llamado r (el módulo y el indicador de exportación no se comprueban, como en el runtime de OTP) | {badrecord, X}; campo ausente {badfield, {{Mod, r}, f}} |
Acceso externo X#m:r.f | Módulo m, nombre r, exportado | {badrecord, X}; {badfield, ...} |
Acceso anónimo X#_.f | Cualquier record nativo (la exportación no se comprueba, como en el runtime de OTP) | {badrecord, X}; {badfield, ...} |
Actualización local X#r{...} | Módulo M, nombre r | {badrecord, X}; campo desconocido {badfield, ...} |
Actualización externa X#m:r{...} | Exportado, módulo m, nombre r | igual |
Actualización anónima X#_{...} | Exportado o módulo M | igual |
Patrón local #r{...} | Módulo M, nombre r, todos los campos enumerados presentes | no encaja |
Patrón externo #m:r{...} | Módulo m, nombre r; exportado cuando se enumera un campo | no encaja |
Patrón anónimo #_{...} | Cualquier record nativo; exportado o módulo M cuando se enumera un campo | no encaja |
- La construcción evalúa los campos dados en orden de código fuente (los valores por defecto son literales); una actualización evalúa primero el record y después los nuevos valores en orden de código fuente, a diferencia de una actualización de record de tupla. Las actualizaciones vacías también comprueban.
- Guards: solo el acceso a campos (un fallo hace fallar el guard) e
is_record. La construcción en un guard dacreating a record in a guard is only supported for tuple records; la actualización daillegal guard expression. is_record(X)solo es verdadero para records nativos.is_record(X, r)con unrnativo local comprueba el móduloMy el nombrer; conrimportado demcomprueba el módulom.is_record(X, m, r)con átomos comprueba módulo y nombre; con un entero es la prueba de tupla.- Los records nativos no son tuplas:
is_tuplees falso; los servicios de elemento y tamaño los rechazan.
Reglas en tiempo de compilación
Los mensajes siguen a erl_lint:
- Los valores por defecto deben ser literales tras el plegado de constantes
(números, átomos, cadenas,
[], conses/tuplas/maps de ellos, binaries solo de cadenas, y los operadores aritméticos, de comparación ynot/and/or/xorsobre ellos, plegados como los pliega el compilador de OTP);++,--,andalso,orelsey un operador que falla (1 / 0) no están permitidos:illegal default value for field a in native record r. - Construcción local sin valor para un campo sin valor por defecto:
field a is not initialized in native record r; campo desconocido:field zz undefined in record r. Los campos desconocidos en accesos, actualizaciones y patrones se aceptan (OTP solo advierte); fallan en tiempo de ejecución. - El índice
#r.a,record_info/2, los inicializadores_ = Vy los refinamientos de tipo de campo son exclusivos de los records de tupla, con los mensajes de OTP. - Un nombre es o bien un record de tupla, o un record nativo, o un record
importado; los conflictos y las formas
-export_record/-import_recordincorrectas son errores.-export_recorddebe preceder a las definiciones de funciones. - Las formas externas que nombran un módulo ajeno al lote, un record no definido
o no exportado se compilan y fallan en tiempo de ejecución como en OTP. La
construcción externa se resuelve contra el lote en tiempo de compilación: el
módulo importador hace él mismo el descenso (lowering) de los valores por
defecto literales del módulo que lo define y referencia su descriptor
exportado; los campos desconocidos lanzan
badfieldantes de que un valor ausente lancenovalue(ErrorReason21).
Reflexión
records responde a partir de la definición capturada por un record:
get/2,get_module/1,get_name/1(badargpara un valor que no es un record o un campo ausente),get_field_names/1,is_exported/1({badrecord, X}).create(Module, Name, Fields, #{is_exported => Boolean})construye una definición en tiempo de ejecución con el orden de campos dado, internada por el servidor de código, de modo que las creaciones iguales la comparten; un record compilado y uno creado con el mismo módulo, nombre, indicador de exportación y orden de campos son=:=. Errores en el orden de OTP:{badrecord, Name}para nombres que no son átomos,{badmap, Options},badargpara otras opciones, después una pasada sobre los campos (badargpara un valor que no es un par,{badfield, {{Module, Name}, Field}}para un nombre que no es un átomo), despuésbadargpara un campo repetido.update(Src, Module, Name, Map)conserva la definición de Src:{badrecord, Src},{badrecord, Name}cuando Module y Name no coinciden,{badmap, Map}, después{badfield, ...}en orden de claves.get_definition(Module, Name)decodifica la lista de campos del descriptor:{#{is_exported => Boolean}, Fields},badargpara un record que ningún módulo del programa define.
Impresión y orden
erlang:display/1imprime#m:r{a=1,b=2}: sin espacios, campos en orden de definición (OTP: orden de índice de la tabla de átomos, diferencias).- Orden de términos: tupla < record nativo < map. Los records se comparan por
módulo, nombre, indicador de exportación (false primero), número de campos,
nombres de campo en orden de definición y después valores en orden de
definición.
=:=exige además el mismo orden de campos;==compara los valores numéricamente (el compilador de OTP pliega algunos==sobre records a=:=, diferencias; se sigue la regla del runtime).
Diferencias que se mantienen
El orden de campos al imprimir, el plegado de ==, la construcción externa de
módulos no cargados y las advertencias de records que faltan están registrados
en diferencias.
Clause