wandres.dev
THE ELM ARCHITECTURE · Model, update, view

Elm y el origen de The Elm Architecture

Elm es un lenguaje funcional puro de la familia ML que compila a JavaScript y hace una promesa insólita: cero excepciones en tiempo de ejecución. Esta lección cuenta cómo de ese compromiso con la pureza no nació una librería sino un patrón —The Elm Architecture— que no se impone desde fuera sino que emerge desde dentro del lenguaje. Recorre la historia de Evan Czaplicki, la FRP con señales del Elm temprano y su retirada en la versión 0.17, y explica por qué la tríada de Model, update y view es la forma que la pureza obliga a adoptar, y por qué se convirtió en la referencia de la que beberían Redux, TCA y MVI.

⏱ 16 min

Antes de que existiera Redux, antes de que “flujo unidireccional” fuera una frase de moda, había un lenguaje que no te dejaba hacer las cosas de otra manera. Elm es un lenguaje funcional puro que compila a JavaScript y promete algo que suena a exageración hasta que lo compruebas: cero excepciones en tiempo de ejecución. The Elm Architecture —el patrón del que todos los demás beben— no apareció como una librería que alguien decidió publicar, sino como la forma que ese compromiso con la pureza obliga a adoptar. No es un estilo recomendado; es la única manera en que un programa Elm puede existir. Entender ese matiz —que la arquitectura emergió del lenguaje en vez de imponerse sobre él— es entender por qué resultó tan sólida que otras comunidades acabaron copiándola casi letra por letra.

🎯 Al terminar esta lección sabrás
  • Situar Elm como lenguaje funcional puro de la familia ML que compila a JavaScript.
  • Entender la promesa de cero excepciones en tiempo de ejecución y de dónde nace.
  • Reconstruir la historia: la FRP con señales del Elm temprano y su retirada en la 0.17.
  • Explicar por qué TEA emerge de la pureza en lugar de imponerse como convención.

El lenguaje antes que el patrón

Elm, creado por Evan Czaplicki como tema de su tesis en 2012, pertenece a la familia ML —la de Haskell, OCaml y F#—: tipado estático fuerte, inferencia de tipos, datos inmutables por defecto y funciones puras como unidad básica. Su compilador traduce a JavaScript y su terreno es el navegador. Lo que lo distingue no es la sintaxis, sino una restricción que atraviesa todo el lenguaje: en Elm no puedes mutar un valor, no puedes lanzar una excepción y no puedes ejecutar un efecto secundario en medio de una función cualquiera. No es que esté mal visto; es que el lenguaje, literalmente, no ofrece la manera de hacerlo.

De esa restricción sale la promesa que hizo célebre a Elm: cero excepciones en tiempo de ejecución. No hay null ni undefined, así que no existe el error más frecuente del navegador. La ausencia de un valor se modela con un tipo explícito, Maybe, y el compilador te obliga a tratar el caso vacío antes de dejarte compilar.

-- No existe null ni undefined: la ausencia es un tipo de datos
type Maybe a
    = Just a
    | Nothing

-- El compilador RECHAZA el codigo si no manejas el caso Nothing
nombreVisible : Maybe String -> String
nombreVisible entrada =
    case entrada of
        Just nombre ->
            nombre

        Nothing ->
            "invitado"

Ese case no es opcional: si olvidas la rama Nothing, el programa no compila. La consecuencia es que la clase entera de errores “cannot read property of undefined” desaparece por construcción. Elm no atrapa esos fallos en producción; los vuelve imposibles de escribir. Y esa promesa no es un eslogan de marketing, sino el efecto agregado de tres decisiones del lenguaje que conviene ver juntas, porque son las mismas que después harán inevitable la arquitectura.

🚫

Sin null ni excepciones

La ausencia se modela con Maybe y el error con Result. El compilador exige tratar ambos casos, así que no hay null que reviente en producción ni excepción que escape sin manejar.

🧊

Datos inmutables

Ningún valor se muta en su sitio. Transformar un dato es construir uno nuevo, lo que elimina de raíz los bugs de estado compartido que se modifica por sorpresa desde dos lugares.

🧮

Funciones puras

Una función depende solo de sus argumentos y no produce efectos por su cuenta. Leerla es entenderla; no hay contexto oculto que cambie su resultado entre dos llamadas idénticas.

Estas tres no son características independientes que Elm reunió por gusto: son un paquete que se refuerza a sí mismo. Sin mutación, el estado tiene que viajar como valor de retorno; sin efectos ocultos, la lógica tiene que ser una transformación pura de entradas a salidas; sin null, cada caso posible tiene que estar representado en un tipo. Cuando las tres rigen a la vez, la única manera de estructurar un programa interactivo es la que veremos: un estado, una transición y una vista. Guárdate esta idea, porque es la que cierra la lección.

De las señales a la arquitectura

El Elm temprano no se vendía como una arquitectura, sino como programación reactiva funcional —FRP—. Su primitivo central era la señal (Signal): un valor que varía en el tiempo y del que otros valores dependen, como una hoja de cálculo elevada a lenguaje. La posición del ratón era una Signal, el reloj era una Signal, y tu interfaz se describía como una red de señales que se recalculaban solas. Era elegante en el papel y difícil en la práctica: costaba de enseñar, costaba de optimizar y empujaba a los principiantes hacia un muro conceptual antes de dibujar nada útil.

En 2016, con la versión 0.17 y un artículo titulado A Farewell to FRP, Czaplicki hizo algo audaz: eliminó las señales del lenguaje por completo. En su lugar quedaron las suscripciones (Sub), una forma más humilde y directa de recibir eventos del mundo. Lo revelador es qué sobrevivió a la amputación. La maquinaria reactiva se fue; la tríada Model, update y view —que había crecido casi sin querer alrededor de las señales— se quedó, y resultó ser lo único que de verdad importaba. La arquitectura sobrevivió al mecanismo que la había engendrado.

Lo que en el Elm temprano era una Signal continua del reloj, en el Elm actual es una suscripción que pides como un dato y que el runtime traduce en mensajes discretos. La diferencia es enorme para el que aprende: ya no modelas el tiempo como un flujo que se propaga, sino que declaras “avísame cada segundo con este Msg” y lo tratas en update como cualquier otro evento.

-- Elm actual: el tiempo entra como Msg discretos, no como una Signal continua
subscriptions : Model -> Sub Msg
subscriptions _ =
    Time.every 1000 Tic

Ese cambio, de valores que varían en el tiempo a mensajes que llegan, es justo lo que volvió el modelo enseñable: todo —un clic, una respuesta HTTP, un tic de reloj— entra por la misma puerta, el tipo Msg, y se trata en el mismo sitio, update.

ℹ️
La retirada de la FRP fue una lección de diseño

Renunciar a la idea más vistosa del lenguaje —las señales— para quedarse con la más modesta —tres funciones y un tipo de estado— es un acto de madurez de diseño poco común. Czaplicki observó que la gente no aprendía Elm por la FRP, sino a pesar de ella, y que el patrón útil ya vivía debajo. Quitar lo brillante para dejar lo esencial es lo contrario de casi toda evolución de frameworks, que tiende a acumular. Esa poda es la razón de que TEA sea tan pequeña que cabe en una pizarra.

📝
El estado de Elm en 2026: nicho, estable, influyente

Conviene calibrar las expectativas. Elm no es una apuesta mayoritaria para producción: su ritmo de versiones es lento, su interoperabilidad con JavaScript pasa por puertos deliberadamente estrechos y su comunidad es pequeña frente a la de React o Vue. Nada de eso resta valor a estudiarlo, porque su importancia nunca fue de cuota de mercado sino de ideas. Elm es el lugar donde el patrón se ve sin ruido, con la garantía de cero excepciones intacta, y por eso funciona como referencia conceptual aunque tu trabajo diario ocurra en otro lenguaje. Aprender Elm no es apostar por Elm; es ir a la fuente de un patrón que usarás disfrazado el resto de tu carrera.

La pureza obliga a la forma

Aquí está la idea que hay que llevarse de esta lección. En un lenguaje impuro —JavaScript, digamos— una “arquitectura” es una convención que puedes violar: nada te impide llamar a fetch dentro del render, mutar un objeto global desde un manejador de eventos o guardar estado escondido en una variable de módulo. La disciplina depende de tu voluntad, y la voluntad falla. En Elm no hay voluntad que valga, porque el lenguaje ha retirado las opciones impuras de la mesa.

Piénsalo por eliminación. Si update no puede mutar nada y no puede ejecutar efectos, lo único que le queda por hacer es tomar el estado viejo y devolver uno nuevo: es forzosamente Msg -> Model -> Model. Si view no puede guardar estado propio ni escribir en el mundo, no le queda más que ser una función pura del modelo: Model -> Html Msg. La forma de la arquitectura no se elige; es el residuo que queda cuando el lenguaje elimina todo lo impuro. Por eso decimos que TEA no se impone sobre Elm: se destila de él.

flowchart TD
P[Lenguaje puro sin mutacion ni efectos] --> R1[update sin mutacion devuelve Model nuevo]
P --> R2[view sin estado propio solo lee Model]
P --> R3[los efectos salen como datos]
R1 --> F[La forma Model update view es el unico residuo posible]
R2 --> F
R3 --> F
style P fill:#cba6f7,color:#11111b
style F fill:#a6e3a1,color:#11111b

Esta diferencia entre convención violable y forma forzada explica un fenómeno posterior: cuando Redux, TCA o MVI adoptaron el patrón en lenguajes impuros, tuvieron que reintroducir con reglas de estilo, linters y revisiones de código la disciplina que a Elm le regalaba el compilador. Copiaron la forma, pero heredaron la fragilidad de tener que sostenerla a mano.

Merece la pena nombrar la inversión conceptual completa, porque cuesta acostumbrarse a ella. En la tradición imperativa, la arquitectura es algo que el programador impone sobre un lenguaje que, por sí mismo, le dejaría hacer cualquier cosa; el lenguaje es permisivo y el orden lo pones tú. En Elm ocurre lo contrario: el lenguaje es restrictivo y la arquitectura es lo que queda cuando agotas las pocas cosas que te deja hacer. No construyes el orden encima del caos; descubres el único orden que el caos, ya prohibido, permite. Por eso un principiante en Elm no “aprende The Elm Architecture” como quien aprende un patrón de diseño opcional: la reinventa sin querer, porque el compilador lo va empujando hacia la única forma que compila. Enseñar TEA es, casi, dejar de estorbar a alguien que ya iba a llegar allí.

TEA no es un patrón que Elm eligió, sino una forma que Elm no pudo evitar

La confusión de fondo, y la que separa a quien entiende TEA de quien solo la usa, es creer que The Elm Architecture es una decisión de diseño entre varias posibles. No lo es. Es un teorema. Si aceptas como axiomas que el estado es inmutable, que las funciones son puras y que los efectos no pueden ocurrir en cualquier sitio, entonces la conclusión —hay un único valor de estado, una única función que lo transforma y una única función que lo dibuja— se sigue con necesidad lógica, no con preferencia estética. Elm no inventó ese teorema; lo hizo inevitable al fijar los axiomas en el lenguaje. Y aquí está lo profundo: precisamente porque es una consecuencia y no una moda, el mismo teorema se redescubre una y otra vez en comunidades que ni siquiera se hablan entre sí. Dan Abramov llega a él desde JavaScript y lo llama Redux; los de Point-Free lo alcanzan desde Swift y lo llaman TCA; los de Android lo reencuentran y lo llaman MVI. Ninguno está copiando una tendencia; todos están tropezando con la misma estructura matemática que aparece en cuanto tomas la pureza en serio. Por eso Elm importa aunque casi nadie lo despliegue en producción: no es una herramienta que compita con React, es el laboratorio donde el patrón se demostró en su forma pura, sin las concesiones que la impureza de otros lenguajes obliga a hacer. Estudiar TEA en Elm es estudiar la idea desnuda, antes de que el mundo real la vistiera de middleware, thunks y hooks. Quien la ha visto así reconoce la misma silueta debajo de cada arquitectura de estado que encontrará el resto de su carrera, y deja de aprender frameworks para empezar a reconocer instancias de una sola idea.

⚔️ Rastrea la forma hasta su causa
  1. Instala Elm y compila el contador canónico. Introduce a propósito un acceso a un valor que podría no existir y observa cómo el compilador te obliga a manejar Maybe antes de dejarte seguir.
  2. Busca el artículo A Farewell to FRP y resume en tres frases por qué Czaplicki eliminó las señales. Relaciónalo con la idea de podar lo brillante para dejar lo esencial.
  3. Escribe en JavaScript un manejador de eventos que viole el patrón —que mute estado global desde el render— y comprueba que el lenguaje te lo permite. Explica por qué en Elm ese mismo código no compilaría.
  4. Argumenta con tus palabras la frase “TEA es un teorema, no una decisión”. Si no puedes derivar la forma Model / update / view a partir de la pureza, aún la ves como convención.
  5. Enumera tres lenguajes o frameworks que hayan adoptado el patrón y anota, para cada uno, qué disciplina tuvieron que reintroducir a mano por no tener un compilador puro.