Atributos de módulo

Los atributos de módulo son anotaciones tipo clave-valor que empiezan por una arroba (como doc, moduledoc, ya vistos) y que sirven para asignar metadatos a los módulos, aunque también son empleados para crear constantes.

Los atributos de módulo en Elixir son una herramienta muy versátil que funciona como anotaciones o metadatos en forma de clave-valor, definidos en tiempo de compilación. Estos atributos permiten almacenar información que el compilador extrae para distintos propósitos, como generar documentación, definir tipos o manejar configuraciones internas del módulo.

Un ejemplo claro que ya conocemos es el uso de @doc y @moduledoc, que sirven para documentar funciones y módulos respectivamente. Estas anotaciones son procesadas por herramientas que generan documentación automáticamente. Pero más allá de eso, existen otros atributos que podemos usar para diferentes fines, algunos heredados del mundo de Erlang, como @vsn, que indica la versión del módulo. Este atributo es útil para controlar versiones y evitar incompatibilidades cuando se actualiza el código, aunque hay que tener cuidado de no romper dependencias al cambiarlo.

Además, podemos definir nuestros propios atributos personalizados. Por ejemplo, si declaramos @mundo "world", el compilador tratará @mundo como una constante cuyo valor es "world". Esto significa que en cualquier parte del módulo donde usemos @mundo, el compilador reemplazará esa referencia por el valor literal en tiempo de compilación. Así, si cambiamos el valor a 5, todas las referencias a @mundo pasarán a ser 5 sin necesidad de modificar el código en múltiples lugares.

Este mecanismo es muy útil para definir constantes o parámetros de configuración que queremos mantener centralizados y evitar duplicaciones. Por ejemplo, podemos crear un atributo @mapa que contenga un mapa con configuraciones como host, puerto, usuario y contraseña:

@mapa %{
  host: "localhost",
  port: 3999,
  user: "admin",
  password: "admin"
}

Luego, en cualquier función del módulo, podemos usar @mapa para acceder a esta configuración. Sin embargo, es importante entender que el compilador no hace un simple copia y pega del valor en el código generado. En realidad, cuando definimos una función que devuelve esta configuración, el código resultante en la máquina virtual BEAM es una función que retorna el mapa, y las referencias a @mapa se traducen en llamadas a esa función. Esto implica que la semántica de usar un atributo como constante no es exactamente igual a definir una función que devuelve un valor constante, aunque en la práctica puede ser muy similar.

Otra característica interesante de los atributos de módulo es que pueden actualizarse durante la compilación. Por ejemplo, podemos definir un atributo @lista como una lista vacía inicialmente:

@lista []

Luego, en otra parte del módulo, podemos actualizarlo agregando elementos:

@lista [2 | @lista]

Esto hace que el valor de @lista pase a ser [2]. Podemos comprobarlo con funciones que impriman el valor de @lista en distintos momentos, y veremos cómo cambia conforme se actualiza. Esta capacidad de modificar atributos durante la compilación es un truco muy utilizado en librerías para construir listas o colecciones de datos en tiempo de compilación, aunque entra en el terreno de la metaprogramación, que es un tema más avanzado.

Un caso práctico de esta actualización es el funcionamiento interno de @doc. Cada vez que usamos @doc para documentar una función, en realidad estamos sobrescribiendo el valor del atributo para asociar la documentación correcta a cada función. El compilador, mediante cierta magia interna, logra enlazar cada documentación con su función correspondiente, lo que es fundamental para herramientas que extraen documentación automáticamente.

En resumen, los atributos de módulo en Elixir son anotaciones que nos permiten almacenar metadatos, constantes o configuraciones, y que el compilador procesa para distintos fines. Podemos definir nuestros propios atributos, usarlos como constantes, actualizarlos durante la compilación y aprovecharlos para mejorar la organización y claridad de nuestro código. Aunque su uso puede parecer sencillo, detrás hay detalles importantes sobre cómo el compilador los maneja y cómo afectan la semántica del código.

Lista de reproducción
  1. 1
    mix
    10 minutos
  2. 2
    Documentando código: comentarios, docs y moduledocs
    10 minutos
  3. 3
    Atributos de módulo
    9 minutos
  4. 4
    Dependencias
    12 minutos
  5. 5
    Un ejemplo práctico de módulo útil
    13 minutos
  6. 6
    Alias e import
    10 minutos
  7. 7
    Sobre las macros, require y use
    11 minutos
  8. 8
    Typespecs (parte 1, usando tipos básicos)
    10 minutos
  9. 9
    Typespecs (parte 2, tipos propios y t())
    11 minutos
  10. 10
    Comportamientos
    11 minutos
  11. 11
    Tratamiento de errores con rescue
    8 minutos
  12. 12
    Elevando errores con raise
    8 minutos
  13. 13
    with
    14 minutos
  14. 14
    Sigilos
    8 minutos
  15. 15
    Tests con ExUnit
    12 minutos
  16. 16
    Más particularidades de ExUnit
    13 minutos
  17. 17
    Microservicios en Elixir con Plug
    11 minutos
  18. 18
    Cómo Plug.Router te ayuda a escribir microservicios en Elixir
    14 minutos
  19. 19
    ¿Cómo hacer rutas dinámicas en Phoenix y Plug?
    13 minutos