⏱️ Lectura: 12 min

José Valim anunció el 3 de junio de 2026 la versión 1.20 de Elixir y, con ella, el lenguaje cruza una frontera que llevaba años preparándose: ya es un lenguaje de tipado gradual. El compilador ahora infiere y verifica tipos en cada programa sin pedir una sola anotación, y reporta código muerto y bugs verificados: violaciones que están garantizadas a fallar en tiempo de ejecución si se ejecutan.

📑 En este artículo
  1. TL;DR
  2. Qué pasó: Elixir 1.20 verifica tipos sin anotaciones
  3. De la investigación a la práctica: cuatro años de tipos set-teóricos
  4. El tipo dynamic(): el corazón del tipado gradual de Elixir
    1. Compatibilidad: solo reporta bugs garantizados
    2. Refinamiento: cómo encuentra bugs reales
  5. Datos y cifras: 12 de 13 en el benchmark de narrowing
  6. Cómo instalar y probar Elixir 1.20
  7. Impacto y análisis para los equipos en LATAM
  8. Qué sigue
  9. Preguntas frecuentes
    1. ¿Tengo que escribir anotaciones de tipo en Elixir 1.20?
    2. ¿En qué se diferencia dynamic() de any()?
    3. ¿Qué es un “bug verificado”?
    4. ¿Actualizar a 1.20 rompe mi código existente?
    5. ¿Qué tan confiable es la detección?
  10. Referencias

Lo llamativo no es solo que Elixir tenga tipos, sino cómo lo logra: con una tasa de falsos positivos extremadamente baja y sin fricción para quien programa. Acá desglosamos qué cambió, cómo funciona el tipo dynamic() y por qué importa para tu próximo proyecto.

TL;DR

  • Elixir 1.20 salió el 3 de junio de 2026: infiere y verifica tipos en todo el código sin requerir anotaciones.
  • El tipo dynamic() solo reporta una violación cuando los tipos aceptados y provistos son disjuntos: así evita falsos positivos.
  • El compilador detecta código muerto y bugs verificados, garantizados a fallar en runtime si se ejecutan.
  • Pasa 12 de 13 categorías del “If T: Benchmark for Type Narrowing”, recuperando tipos precisos de código común.
  • El sistema es sound, gradual y set-teórico: se compone con uniones, intersecciones y negaciones de tipos.
  • El esfuerzo arrancó en 2022 y un paper premiado en 2023 fundó el diseño; CNRS y Remote lo hicieron posible.
  • Fresha y Tidewave patrocinan el desarrollo actual del sistema de tipos.

Qué pasó: Elixir 1.20 verifica tipos sin anotaciones

Durante años, Elixir fue un lenguaje dinámico orgulloso de serlo. Su seguridad venía de patrones como el ajuste de patrones (pattern matching), las guías (guards) y la filosofía “let it crash” heredada de Erlang y la BEAM. La versión 1.20 no rompe nada de eso, pero suma una capa nueva: un sistema de tipado gradual que trabaja en silencio durante la compilación.

La meta del primer hito fue deliberadamente conservadora y, por eso mismo, poderosa: hacer que el sistema de tipos aporte valor sin obligar a escribir anotaciones. En lugar de pedirte que declares @spec en cada función, el compilador deduce los tipos a partir de cómo usás cada variable y, con esa información, encuentra dos clases de problemas: código muerto (ramas que nunca se ejecutan) y bugs verificados (operaciones que reventarían en runtime).

La distinción entre “bug posible” y “bug verificado” es clave. Un verificador estático tradicional rechaza muchos programas válidos porque no puede capturar la intención exacta del autor. Elixir, en cambio, decide reportar solo aquello que no tiene salvación: si la operación se ejecuta, la máquina virtual lanza una excepción sí o sí. Esa decisión de diseño es lo que mantiene la confianza alta y los falsos positivos bajos.

De la investigación a la práctica: cuatro años de tipos set-teóricos

El camino no fue corto. En 2022 el equipo anunció el esfuerzo de añadir tipos set-teóricos al lenguaje. En junio de 2023 publicó un paper premiado sobre el diseño del sistema de tipos y declaró que el trabajo pasaba de la investigación al desarrollo. Elixir 1.20 es la primera entrega concreta de esa fase de desarrollo.

Línea de tiempo del tipado gradual en Elixir desde 2022
De anuncio en 2022 a lenguaje gradualmente tipado en 2026.

El sistema se apoya en tres objetivos declarados. Primero, ser sound (consistente): los tipos que el sistema infiere y asigna se alinean con el comportamiento real del programa. Segundo, ser gradual: incluye el tipo dynamic() para los casos en que el tipo se decide recién en tiempo de ejecución; en su ausencia, el sistema se comporta como uno estático. Tercero, ser amigable: los tipos se describen con operaciones básicas de conjuntos — uniones, intersecciones y negaciones — con mensajes de error claros. De ahí el nombre “set-teórico”.

Que sea posible no fue gratis. El sistema nació de una alianza entre el CNRS (el centro nacional de investigación científica de Francia) y la empresa Remote. El desarrollo actual está patrocinado por Fresha y Tidewave, un detalle que importa: muestra que el ecosistema empresarial está financiando mejoras de fondo, no solo features cosméticas.

El tipo dynamic(): el corazón del tipado gradual de Elixir

Muchos sistemas graduales tienen un tipo any() que, en la práctica, significa “todo vale”: nada se reporta. Elixir eligió otro camino. Su tipo gradual se llama dynamic() y tiene dos propiedades que cambian todo: compatibilidad y refinamiento (narrowing).

Compatibilidad: solo reporta bugs garantizados

En un sistema estático clásico, si una variable tiene tipo integer() or binary() y se la pasa a una función, esa función debe aceptar ambos tipos. Pero como los tipos no capturan la intención completa de cada programa, esto genera falsos positivos. Mirá este ejemplo:

def porcentaje_o_error(valor) when is_integer(valor) do
  valor_o_error =
    if valor > 1 do
      valor
    else
      "no salió bien"
    end

  # ... más código ...

  if valor > 1 do
    valor_o_error / 100
  else
    String.upcase(valor_o_error)
  end
end

Aunque valor_o_error tiene tipo integer() or binary(), el operador / acepta solo números y String.upcase acepta solo cadenas. El programa es válido y nunca lanza una excepción, porque la rama que divide solo corre cuando el valor es entero y la que pasa a mayúsculas solo cuando es cadena. Un verificador estático ingenuo reportaría dos violaciones falsas.

Elixir, en cambio, etiqueta a valor_o_error con el tipo dynamic(integer() or binary()): el tipo será entero o cadena en runtime. Al llamar una función con un tipo dynamic(), Elixir emite una violación solo si los tipos provistos y los aceptados son disjuntos. Como integer() sí es compatible con lo que espera /, no hay violación. Esto es la propiedad de compatibilidad.

Ahora comparálo con un bug de verdad:

valor_o_error =
  if valor > 1 do
    valor
  else
    "no salió bien"
  end

Map.fetch!(valor_o_error, :alguna_clave)

Aquí Map.fetch! espera un mapa, pero valor_o_error solo puede ser entero o cadena en runtime. Los tipos aceptados y provistos son completamente disjuntos: jamás hay intersección. Eso sí es un bug verificado, garantizado a explotar en ejecución. Y eso es exactamente lo que el compilador reporta.

💭 Clave: La diferencia entre any() y dynamic() es la que separa un linter ruidoso de uno en el que confías. Solo se queja cuando el error es matemáticamente inevitable.

Refinamiento: cómo encuentra bugs reales

Reportar solo bugs garantizados no serviría de mucho si el sistema casi nunca encontrara alguno. La segunda propiedad, el refinamiento, resuelve eso: el tipo dynamic() se va estrechando según cómo se usa la variable. Observá:

def suma_a_y_b(data) do
  data.a + data.b
end

data empieza como dynamic(). Al usarlo como data.a y data.b dentro del operador +, Elixir refina data al tipo %{..., a: number(), b: number()}: un mapa que tiene al menos los campos a y b con valores numéricos. A partir de ahí, cualquier llamada que contradiga esa forma inferida se vuelve un bug verificado. Así el sistema gana poder de detección sin que escribas un solo @spec.

graph TD
  A["dynamic(integer() or binary())"] --> B{"¿Tipos disjuntos?"}
  B -->|"Sí"| C["Violación: bug verificado"]
  B -->|"No"| D["Sin violación: compatible"]

Datos y cifras: 12 de 13 en el benchmark de narrowing

Para medir qué tan bien recupera tipos precisos del código cotidiano, el equipo usó el “If T: Benchmark for Type Narrowing”. Elixir 1.20 pasa 12 de sus 13 categorías. Es una señal concreta de que el motor de inferencia puede extraer información de tipos útil de programas escritos de forma idiomática, sin que nadie haya anotado nada de antemano.

Tabla conceptual del benchmark de narrowing con tipado gradual en Elixir
El motor recupera tipos en 12 de 13 categorías del benchmark.

La otra cifra que vale la pena resaltar es cualitativa: la tasa de falsos positivos extremadamente baja. En la adopción de cualquier sistema de tipos sobre código existente, los falsos positivos son el principal motivo de abandono. Si el compilador grita en cada compilación por cosas que en realidad funcionan, los equipos terminan ignorándolo. Al limitar los reportes a bugs verificados, Elixir apuesta a que cada advertencia que veas sea, casi siempre, un problema real.

Cómo instalar y probar Elixir 1.20

Probar la novedad no requiere migrar nada: el chequeo gradual funciona sobre tu código actual. Estos son los comandos para instalar la última versión en cada sistema operativo.

# macOS (Homebrew)
brew install elixir

# Linux (Debian / Ubuntu)
sudo apt-get update && sudo apt-get install -y elixir

# Linux / macOS con asdf (recomendado para manejar varias versiones)
asdf plugin add elixir
asdf install elixir 1.20.0-otp-27
asdf global elixir 1.20.0-otp-27
# Windows (Chocolatey)
choco install elixir

# Windows (Scoop)
scoop install elixir

Luego verificá la versión y compilá tu proyecto para que el chequeo gradual entre en acción:

elixir --version
mix compile --force
💡 Tip: Compilá con mix compile --force en una rama limpia y revisá las advertencias nuevas. Como son bugs verificados, suelen apuntar a código muerto o ramas imposibles que conviene limpiar antes de tu próximo release.

Impacto y análisis para los equipos en LATAM

Para la comunidad hispana que usa Elixir — muy presente en startups con Phoenix y LiveView — este hito tiene tres lecturas prácticas. La primera es de mantenimiento: bases de código grandes acumulan ramas muertas y suposiciones rotas que ahora salen a la luz solo con recompilar, sin reescribir nada. Es una auditoría gratis de calidad.

La segunda es cultural. Elixir demuestra que se puede añadir un sistema de tipos a un lenguaje dinámico maduro sin partir a la comunidad en dos, como sí ocurrió en otros ecosistemas donde los tipos llegaron como una capa externa y opcional. Acá el tipado gradual viene de fábrica, integrado al compilador, y no te obliga a cambiar tu estilo.

La tercera es estratégica para quien decide tecnologías. Un argumento recurrente contra los lenguajes dinámicos en proyectos grandes es la falta de garantías en tiempo de compilación. Con 1.20, ese argumento pierde fuerza: Elixir ofrece detección de errores reales sin el costo de anotar todo, un punto medio que muchos equipos venían pidiendo. Y como el roadmap apunta a permitir anotaciones explícitas más adelante, la base ya está puesta.

📌 Nota: El tipado gradual de 1.20 no reemplaza a tus tests ni a las guías con pattern matching. Es una red de seguridad adicional que atrapa una clase específica de errores: los garantizados a fallar.

Qué sigue

Este es el primer hito de desarrollo, no el final del camino. La hoja de ruta del equipo apunta a sumar, en versiones futuras, la posibilidad de escribir anotaciones de tipo explícitas para que el sistema verifique más contratos y reporte más problemas, manteniendo siempre la filosofía gradual: lo que no anotes seguirá funcionando como hasta ahora. La meta de fondo es que el día que quieras más rigor, el lenguaje esté listo, y el día que no, no te estorbe.

Mientras tanto, la recomendación para los equipos es simple: actualizar, recompilar y leer con atención las advertencias nuevas. Cada una representa, con muy alta probabilidad, un bug real esperando a manifestarse en producción.

📖 Resumen en Telegram: Ver resumen

Preguntas frecuentes

¿Tengo que escribir anotaciones de tipo en Elixir 1.20?

No. El primer hito del sistema de tipos funciona sin anotaciones: el compilador infiere los tipos a partir del uso de cada variable y reporta solo bugs verificados y código muerto. Las anotaciones explícitas llegarán en versiones futuras como opción.

¿En qué se diferencia dynamic() de any()?

El tipo any() de otros sistemas graduales suele significar “todo vale” y no reporta nada. dynamic() tiene compatibilidad (reporta solo cuando los tipos son disjuntos) y refinamiento (se estrecha según el uso), lo que permite hallar bugs reales con pocos falsos positivos.

¿Qué es un “bug verificado”?

Es una violación de tipos que está garantizada a fallar en tiempo de ejecución si esa línea se ejecuta — por ejemplo, pasar un entero a Map.fetch!, que espera un mapa. Elixir reporta estos casos, no los “posibles” errores que un programa válido podría evitar en runtime.

¿Actualizar a 1.20 rompe mi código existente?

El chequeo gradual no cambia el comportamiento en runtime de tu programa. Puede mostrar advertencias nuevas sobre código muerto o bugs verificados, pero la semántica de ejecución se mantiene. Conviene recompilar con mix compile --force y revisar lo que aparezca.

¿Qué tan confiable es la detección?

El sistema pasa 12 de 13 categorías del “If T: Benchmark for Type Narrowing” y declara una tasa de falsos positivos extremadamente baja, justamente porque se limita a reportar bugs garantizados.

Referencias

📱 ¿Te gusta este contenido? Únete a nuestro canal de Telegram @programacion donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.

Categorías: Noticias Tech

Andrés Morales

Desarrollador e investigador en inteligencia artificial. Escribe sobre modelos de lenguaje, frameworks, herramientas para devs y lanzamientos open source. Cubre papers de ML, ecosistema de startups tech y tendencias de programación.

0 Comentarios

Deja un comentario

Marcador de posición del avatar

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.