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.
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.
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()ydynamic()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.
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 --forceen 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
- elixir-lang.org — Anuncio oficial de Elixir v1.20 por José Valim (fuente primaria).
- github.com/elixir-lang/elixir — Repositorio oficial del lenguaje, releases y changelog.
- hexdocs.pm/elixir — Documentación oficial de la biblioteca estándar de Elixir.
- elixir-lang.org — Sitio oficial: instalación, guías y aprendizaje.
📱 ¿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.
Top comments (0)