wandres.dev
NIVEL DIOS: SÍNTESIS · pensar funcionalmente

Seguir aprendiendo: la comunidad, los frameworks, los recursos clave y un plan de dominio continuo

Terminar un track no es terminar de aprender, y en el caso de Elm la continuación tiene una particularidad que conviene entender antes de buscar el siguiente recurso: el ecosistema es deliberadamente pequeño, deliberadamente lento y está gobernado por decisiones de diseño que en otros lenguajes serían impensables. Esta lección explica cómo funciona realmente ese ecosistema —el versionado semántico impuesto por el compilador, el catálogo de paquetes cerrado, la ausencia de novedades como política y no como abandono— y qué esperar de él de forma realista. Después ordena los recursos que de verdad enseñan algo, en la secuencia en que conviene consumirlos y no en la que aparecen listados. Recorre el terreno de los frameworks, desde elm-spa y su sucesor Elm Land hasta elm-pages y Lamdera, con criterio sobre cuándo adoptar uno y cuándo escribir el enrutador a mano. Y termina con lo único que convierte el conocimiento en dominio: un plan de práctica continuada con proyectos de dificultad creciente y con la lectura de código ajeno como disciplina central.

⏱ 21 min

Hay una experiencia común a casi todo el que llega a Elm desde el ecosistema de JavaScript, y es una forma leve de desconcierto. Se busca el paquete de moda y no existe. Se mira la fecha del último lanzamiento del compilador y hace años que no cambia. Se pregunta en un foro por la hoja de ruta y la respuesta es que no hay hoja de ruta pública. Alguien concluye entonces que el lenguaje está abandonado y se marcha, y comete un error de lectura que vale la pena deshacer, porque lo que está observando no es el síntoma de una comunidad que se apaga sino la consecuencia visible de un conjunto de decisiones tomadas a propósito. Elm decidió que la estabilidad es una característica y no una falta de actividad; que un catálogo pequeño y curado vale más que uno enorme y desigual; que el versionado semántico debe imponerlo una herramienta y no la buena voluntad de los autores. Esta lección explica ese ecosistema tal como es, señala los recursos que enseñan de verdad, sitúa los frameworks disponibles con criterio para elegir, y propone el único método conocido para pasar de saber a dominar, que consiste en construir y en leer con constancia durante bastante más tiempo del que a nadie le gustaría.

🎯 Al terminar esta lección sabrás
  • Interpretar correctamente la estabilidad del ecosistema de Elm y las decisiones de diseño que la producen, distinguiéndolas del abandono.
  • Ordenar los recursos disponibles por utilidad real y consumirlos en la secuencia que corresponde al punto en que estás.
  • Elegir con criterio entre escribir el enrutador a mano, adoptar un framework como Elm Land o elm-pages, o irse a una plataforma como Lamdera.
  • Diseñar un plan de dominio continuo con proyectos de dificultad creciente y lectura sistemática de código ajeno.

Un ecosistema pequeño, estable y con reglas propias

El rasgo que más desconcierta es el ritmo. El compilador lleva mucho tiempo sin cambios de versión mayor, y esa quietud es objeto de un debate genuino dentro y fuera de la comunidad: hay quien la considera la mayor virtud del proyecto y quien la considera un riesgo de adopción, y ambas posturas tienen argumentos que merecen escucharse sin caer en la caricatura. Lo que no admite discusión es el efecto práctico: una aplicación Elm escrita hace varios años compila hoy sin tocar una línea, y en un oficio donde la mitad del esfuerzo de mantenimiento se va en perseguir cambios rompedores de dependencias, eso tiene un valor económico que no suele contabilizarse hasta que se pierde.

El catálogo de paquetes funciona con reglas propias que conviene conocer. Todo paquete publicado obedece a un versionado semántico que el compilador calcula comparando las firmas expuestas entre dos versiones, de modo que un autor no puede publicar como parche un cambio que rompe el tipo de una función aunque quiera. Los paquetes no pueden contener JavaScript arbitrario salvo un conjunto muy reducido y controlado, lo cual explica a la vez por qué el catálogo es más pequeño y por qué instalar una dependencia no introduce riesgo de ejecución. La consecuencia cultural es que en Elm se escribe más código propio y se instala menos, y que la pregunta habitual no es qué paquete resuelve esto sino cuántas líneas cuesta resolverlo aquí.

Esa regla tiene un efecto secundario que solo se aprecia después de mantener un proyecto durante un par de años: actualizar dependencias deja de ser una actividad de riesgo. Cuando la herramienta garantiza que una subida de versión menor no puede cambiar ninguna firma expuesta, revisar el registro de cambios se vuelve opcional en lugar de obligatorio, y la práctica habitual en otros ecosistemas de fijar versiones exactas y no tocarlas durante años, que es la causa principal de la deuda de dependencias, aquí carece de sentido.

-- El versionado no lo decide el autor, lo calcula la herramienta.
--
--   parche  cambia solo la implementacion interna
--   menor   anade algo nuevo sin tocar lo existente
--   mayor   cambia o elimina una firma expuesta
--
-- Anadir un constructor a un tipo expuesto es cambio MAYOR,
-- porque rompe el case of exhaustivo de todos los consumidores.
-- Esa consecuencia sorprende la primera vez y es exactamente correcta.
🔱

Foros y voz viva

El Slack de la comunidad y el foro Discourse son donde ocurre la conversación real. La cultura es notablemente paciente con las preguntas de diseño y poco tolerante con las discusiones de gusto.

🎙️

Elm Radio

El podcast de Dillon Kearns y Jeroen Engels es probablemente el recurso avanzado con mejor relación entre tiempo invertido y criterio adquirido. Episodios monográficos sobre decisiones de diseño reales.

📦

El catálogo

Documentación uniforme, versionado impuesto por herramienta y ejemplos ejecutables. Aprende a leer una firma y decidir sin abrir el código fuente.

🛠️

Herramientas

elm-format para cerrar los debates de estilo, elm-test para lo que el compilador no cubre, elm-review para la disciplina de equipo y elm-watch para un ciclo de desarrollo rápido.

Los recursos que importan y en qué orden

La guía oficial es breve y está deliberadamente incompleta: enseña el bucle y se detiene antes de lo interesante, lo cual la hace excelente como primera lectura y decepcionante como referencia. El salto siguiente son los libros. Elm in Action, de Richard Feldman, es el texto que más gente cita como el que hizo clic, porque construye una aplicación real y va introduciendo los conceptos cuando el proyecto los necesita. Programming Elm, de Jeremy Fairbank, cubre parte del mismo terreno con más énfasis en pruebas y en integración con JavaScript. Ninguno de los dos sustituye al recurso que más rinde a partir de este punto, que es leer código ajeno bien escrito.

Hay además una habilidad que casi nadie enseña explícitamente y que a partir de aquí rinde más que cualquier lectura: saber decidir si un paquete sirve leyendo únicamente sus firmas. La documentación del catálogo es uniforme y muestra el tipo de cada función expuesta, y con el criterio que ya tienes esa lista basta para responder las preguntas que importan. Si una función devuelve Cmd msg, sabes que describe un efecto y que el resultado volverá como mensaje. Si devuelve un Result, sabes que el fallo es un caso que tendrás que tratar y no una sorpresa. Si un tipo expuesto no revela sus constructores, sabes que el autor ha cerrado la construcción a propósito y que solo podrás fabricar valores mediante sus funciones, lo cual suele indicar que hay invariantes que proteger. Diez minutos leyendo firmas ahorran una tarde de integración fallida.

Entre las charlas hay tres que funcionan como piezas de formación de criterio más que como tutoriales. Making Impossible States Impossible es la exposición canónica del argumento de modelado que has trabajado durante todo el track. The Life of a File defiende una tesis contraria a la intuición dominante sobre cuándo partir un módulo, y merece verse precisamente si te resulta incómoda. Types Without Borders conecta el sistema de tipos con la generación automática de decoders a partir de esquemas del servidor, que es la técnica que elimina la última fuente de trabajo manual repetitivo en la frontera.

💡
Lee código antes que buscar el siguiente tutorial

Llegado a este nivel, el rendimiento por hora de un tutorial más es bajo y el de leer una aplicación completa es altísimo. Empieza por el ejemplo canónico de aplicación realista escrito por Richard Feldman, que implementa la especificación de una aplicación de blog con autenticación, rutas y formularios, y que fue escrito precisamente como referencia de estructura. Léelo en un orden concreto: primero los tipos del dominio, después Route y el reparto de páginas, después la frontera de red, y solo al final las vistas. Anota cada decisión que te sorprenda y busca su justificación antes de descartarla. Media docena de aplicaciones leídas así te dan un criterio que ningún curso reproduce.

Frameworks y plataformas: qué hay y cuándo adoptarlo

Durante años el proyecto de referencia para aplicaciones de una sola página fue elm-spa, que resolvía el enrutado y la composición de páginas con generación de código y una convención de directorios. Su autor lo dio por concluido y lo sucedió con Elm Land, que persigue el mismo objetivo con un alcance mayor: proyecto inicial completo, rutas basadas en ficheros, capas y una experiencia de desarrollo cuidada. Si vas a arrancar hoy una aplicación con muchas páginas y quieres saltarte la ceremonia del enrutador, ese es el camino con más tracción. En un terreno distinto está elm-pages, orientado a sitios con contenido y generación estática, con carga de datos en tiempo de construcción y una integración fuerte con el sistema de tipos. Y en un terreno más radical está Lamdera, que no es un framework sino una plataforma donde el cliente y el servidor se escriben ambos en Elm, comparten tipos y el estado del servidor persiste sin base de datos explícita, con migraciones de tipo comprobadas por el compilador.

flowchart TD
A[Que estas construyendo] --> B{Cuantas paginas}
B -->|Una o dos| C[Browser element o document a mano]
B -->|Varias con rutas| D{Contenido o aplicacion}
D -->|Sitio con contenido| E[elm-pages]
D -->|Aplicacion interactiva| F[Elm Land]
D -->|Cliente y servidor en Elm| G[Lamdera]
C --> H[Enrutador propio si crece]
F --> I[Convenciones y rutas por fichero]
style C fill:#a6e3a1,color:#11111b
style F fill:#89b4fa,color:#11111b
style G fill:#cba6f7,color:#11111b
-- Lo que un framework de rutas automatiza, escrito a mano una vez.

type Pagina
    = PaginaInicio
    | PaginaInformes Informes.Model
    | PaginaDetalle Detalle.Model
    | PaginaNoEncontrada


type Msg
    = CambioLaUrl Url
    | PidioNavegar UrlRequest
    | MsgInformes Informes.Msg
    | MsgDetalle Detalle.Msg


update : Msg -> Model -> ( Model, Cmd Msg )
update msg modelo =
    case ( msg, modelo.pagina ) of
        ( MsgInformes hijo, PaginaInformes estadoHijo ) ->
            Informes.update hijo estadoHijo
                |> conPagina modelo PaginaInformes MsgInformes

        ( CambioLaUrl url, _ ) ->
            irA (Route.desdeUrl url) modelo

        _ ->
            ( modelo, Cmd.none )

Esa función conPagina que envuelve el modelo hijo y traduce el comando con Cmd.map es literalmente todo lo que un framework de rutas te ahorra escribir, repetido una vez por página. Escribirla a mano cuesta una tarde y enseña más sobre la arquitectura que cualquier lección, porque obliga a entender por qué el mensaje del hijo tiene que envolverse y por qué la rama que combina un mensaje de una página con el estado de otra existe y debe ignorarse en silencio.

El criterio para elegir no es cuál es mejor sino cuánta convención estás dispuesto a aceptar a cambio de cuánta ceremonia te ahorras. Un framework te da estructura el primer día y te cobra el día en que necesitas algo que su convención no previó. En Elm ese cobro es más suave que en otros ecosistemas, porque debajo sigue habiendo un Browser.application con su Model y su update, y siempre puedes bajar un nivel. Aun así, la recomendación que más gente suscribe después de varios proyectos es haber escrito al menos una vez el enrutador y la composición de páginas a mano, aunque después nunca vuelvas a hacerlo, porque solo entonces entiendes qué está automatizando la herramienta y puedes diagnosticar cuando se comporte de forma inesperada.

Un plan de dominio continuo

El conocimiento se convierte en dominio por una vía y solo una: construir cosas de dificultad creciente y someterlas a revisión. Una secuencia que funciona bien empieza por una aplicación de una sola pantalla con estado no trivial, sin red, para consolidar el modelado; sigue con una que consuma un API real y obligue a diseñar decoders y estados de error de verdad; continúa con una aplicación de varias rutas con sesión persistida, que es donde aparecen los problemas interesantes de composición; y culmina con una que integre una biblioteca de JavaScript a través de ports, que es donde se aprende a diseñar una frontera bajo presión. En paralelo, dos disciplinas constantes: leer una aplicación ajena completa al mes y publicar un paquete pequeño, aunque no lo use nadie, porque el versionado impuesto por el compilador enseña sobre diseño de APIs más que cualquier lectura.

-- Un plan de doce semanas, con una sola dificultad nueva por proyecto.
--
--   1 a 2   Una pantalla, estado no trivial, sin red.
--           Dificultad nueva: modelar sin banderas booleanas.
--
--   3 a 5   Consumo de un API real con errores de verdad.
--           Dificultad nueva: decoders y estados de fallo del dominio.
--
--   6 a 9   Varias rutas, sesion persistida, formularios.
--           Dificultad nueva: composicion de paginas y navegacion.
--
--   10 a 12 Integracion con una biblioteca de JavaScript.
--           Dificultad nueva: disenar una frontera bajo presion.
--
-- En paralelo, cada mes: leer una aplicacion ajena entera
-- y publicar o revisar un paquete propio muy pequeno.

La parte del plan que más gente se salta es la revisión, y es justamente la que produce el salto. Construir sin que nadie mire consolida los hábitos buenos y también los malos, y a partir de cierto punto los malos pesan más porque ya están automatizados. Las dos formas de revisión que funcionan sin necesitar un mentor son publicar el código y pedir opinión en el foro de la comunidad, donde la cultura es sorprendentemente generosa con las preguntas de diseño bien formuladas, y releerse a uno mismo con distancia: volver a un proyecto propio tres meses después y anotar cada decisión que hoy tomarías distinta. Ese segundo ejercicio no cuesta nada y es el que mejor mide si has progresado, porque el progreso en diseño no se nota mientras ocurre.

⚠️
Los dos errores de quien intenta continuar solo

El primero es quedarse en el consumo. Ver charlas y leer artículos produce la sensación de progreso sin producir progreso, porque el conocimiento de diseño no se transfiere por exposición sino por decisión bajo restricción. El segundo, más frecuente en gente competente, es elegir siempre proyectos que ya sabe hacer. Un proyecto que no te obliga a rediseñar algo a mitad de camino no te enseñó nada: solo confirmó lo que ya tenías. La regla práctica es que cada proyecto nuevo debe contener exactamente una dificultad que no sepas resolver de antemano. Ni cero, porque entonces es práctica vacía, ni tres, porque entonces se abandona.

Elm no es un destino, es un régimen de entrenamiento, y su rendimiento se cobra sobre todo fuera de él

Conviene cerrar el track diciendo con claridad algo que solo se entiende del todo al final. La probabilidad de que pases el resto de tu carrera escribiendo Elm es baja, y eso no reduce en nada el valor de lo que has hecho, porque el papel histórico de este lenguaje nunca fue conquistar cuota de mercado sino demostrar una tesis y exportarla. La tesis era que una aplicación de interfaz puede construirse sin excepciones en tiempo de ejecución, sin valores nulos y sin escotillas dinámicas, y que hacerlo no cuesta productividad sino que la aumenta a partir del tercer mes. La exportación ya ocurrió y está a la vista de todos: Redux es TEA con otro nombre, la Composable Architecture es TEA en Swift con efectos como valores explícitos, los patrones de intención y modelo en el mundo de Android son la misma figura, la reconciliación declarativa que hoy gobierna la infraestructura es el mismo diff de árboles aplicado a servidores, y la discusión sobre uniones discriminadas y exhaustividad que hoy es rutina en TypeScript llegó allí en buena medida porque un puñado de lenguajes demostró primero que se podía vivir así. Nada de eso significa que aprender Elm haya sido innecesario porque ya está en todas partes; significa exactamente lo contrario. Las versiones diluidas de una idea se aprenden mal, porque se aprenden con sus concesiones incorporadas y sin ver nunca la forma pura de la que son concesión. Quien conoce Redux sin conocer TEA sabe usar una herramienta; quien conoce TEA sabe por qué la herramienta tiene la forma que tiene, y por tanto sabe cuándo su concesión es aceptable y cuándo está destruyendo la propiedad que la justificaba. Eso es lo que te llevas, y es lo que no caduca: no un lenguaje, sino la versión sin diluir de un conjunto de ideas que vas a reconocer disfrazadas durante el resto de tu carrera profesional, en herramientas que todavía no existen y que resolverán problemas que todavía no se han planteado. El siguiente paso, por tanto, no es leer más sobre Elm. Es construir algo real, terminarlo, y después ir a mirar con estos ojos el código que escribes cuando nadie te obliga a escribirlo bien.

⚔️ Convierte el final del track en el principio de una práctica
  1. Escribe tu plan de los próximos tres meses con cuatro proyectos concretos en orden de dificultad, y anota para cada uno la única dificultad nueva que contiene.
  2. Lee entera una aplicación Elm ajena de referencia siguiendo el orden dominio, rutas, frontera, vistas. Anota las cinco decisiones que te sorprendieron y busca la justificación de cada una antes de opinar.
  3. Elige entre escribir tu enrutador a mano o adoptar un framework, y justifica la elección en términos de qué convención aceptas y qué ceremonia evitas. Si nunca lo has hecho a mano, hazlo esta vez.
  4. Publica un paquete diminuto con una sola función bien diseñada y después introduce a propósito un cambio rompedor para ver cómo la herramienta te obliga a subir la versión mayor. Explica qué aprendiste sobre diseño de APIs.
  5. Integra una biblioteca de JavaScript mediante ports diseñando la frontera antes de escribirla, con decodificación estricta en la entrada, y documenta qué garantía protege ese decoder.
  6. Escribe media página explicando a un equipo que no usa Elm qué dos ideas de este track piensas aplicar en su código base la semana que viene, y qué medirás para saber si funcionaron.