Por que un debugger cambia para siempre como se aprende Haskell


5 de mayo de 2026

La pregunta que me hizo Pascual

Esta noche, despues de pelearnos cuatro horas para tener hdb funcionando en Doom Emacs Y en IntelliJ Ultimate, Pascual me dijo: "no se que es un thunk, pero a partir de ahora si que voy a poder aprender bien, no?".

Le dije que si. Y luego me quede pensando en por que esa frase, dicha a las once y media de la noche por un programador PHP que apenas sabe que existen las monadas, contiene la mitad de la historia del lenguaje y casi toda la del problema con su adopcion.

Esto va de eso.

Lo que se ve y lo que se imagina

Si tu has aprendido a programar en Java, Python, PHP, JavaScript, Go, Rust, C, C++, Ruby… has aprendido programando con evaluacion estricta: cuando escribes x = 1 + 2, el ordenador calcula 3, lo guarda, y x apunta a un 3 real en memoria. Si pones un breakpoint, paras, miras x, ves 3. Lo que escribiste en codigo es lo que esta pasando ahora mismo en la maquina.

Esa correspondencia uno-a-uno entre codigo escrito y memoria real la has interiorizado durante miles de horas. Cuando lees un for en Java, ves un puntero al primer elemento moviendose. Cuando ves un array.filter() en JavaScript, sabes que se itera la lista entera y se construye otra lista nueva. La maquina hace exactamente lo que parece estar haciendo.

Aprendiste a programar viendo lo que el codigo hace. El debugger fue tu maestro silencioso.

Ahora viene Haskell.

La diferencia que rompe la intuicion

En Haskell escribes:

let xs = [1..1000000]
    n  = length (filter even xs)
in print n

Y NO PASA NADA hasta que el print n obliga a pasar algo. La lista no se crea. El filter no recorre nada. El length no cuenta. Hasta que algo OBLIGA a evaluar.

Y aun entonces no se hace lo que parece. Ni se reserva un giga de memoria para los millones, ni se construye una lista filtrada intermedia. Lo que pasa es streaming perezoso: length pide elementos a filter, filter pide elementos a [1..1000000], y solo se materializan los necesarios.

Si tu intuicion ha sido formada en lenguajes estrictos, esto te sabe a brujeria. Has visto el codigo y has visto memoria, pero nunca has visto memoria que es promesa de calculo y no calculo hecho. Eso se llama thunk, y el ecosistema Haskell lleva treinta anos ensenandote que existe dibujandolo en pizarra.

La era de las pizarras

Cualquier libro de Haskell tiene un capitulo donde alguien dibuja algo asi:

   xs = take 5 (map (^2) (filter even [1..]))

         |
         v evaluacion forzada por print

         take 5 _____ ________ _____________
                 |        |
         map (^2) ___ filter even __ [1, 2, 3, 4, 5, 6, ...
                                          ^         ^         ^
                                          |         |         |
                                       descarta  acepta   descarta
                                                    |
                                                    v cuadrado
                                                    4

Y el autor te explica: "fijate como take 5 solo pide cinco. Como filter even deja pasar 2, 4, 6, 8, 10. Como map (^2) los eleva. Como solo se procesan los necesarios y la lista de un millon nunca se materializa entera".

Si eres listo y tienes paciencia, lo entiendes. Si lo lees diez veces, lo entiendes mejor. Si lo dibujas tu mismo en una libreta veinte veces, llega un dia en que ya lo ves sin dibujarlo.

Pero eso te toma anos. A mi me tomo dos. A muchos les ha tomado mas, y a otros tantos les ha agotado la paciencia y han tirado el libro.

Y todo porque Haskell te pedia que confiaras en el modelo dibujado en una libreta mientras tu maquina ejecutaba algo que tu no podias inspeccionar.

La excusa elegante

La comunidad Haskell, durante esos treinta anos, tenia una respuesta preparada para cuando alguien preguntaba "y un debugger que pause en el codigo?":

"En Haskell la lazy evaluation hace que step-by-step no tenga sentido. Una expresion solo se evalua cuando la fuerzas. Si pones breakpoint en let x = expensiveComputation, lo unico que pasa es que x apunta a un thunk; expensiveComputation se ejecuta luego, en otro contexto, posiblemente nunca. Por eso usamos types, property tests, y trace. Si necesitas debugger es que tu codigo esta mal pensado."

Era una excusa con base tecnica real. La evaluacion lazy hace que las nociones tradicionales de "linea actual" y "estado en este punto" sean ambiguas. Pero la conclusion ("pues no hay debugger, arreglatelas con tipos") era mas un acto de auto-defensa ideologica que una solucion al problema pedagogico.

Porque el problema pedagogico es real: sin debugger, la mayoria de la gente no puede aprender Haskell. Y la comunidad respondio diciendo "es que no hace falta", lo cual es como decir que si tu hijo no entiende algebra, eso prueba que el algebra es divina y los no-iniciados no estan preparados para ella.

Lo que ha cambiado

Well-Typed (los maintainers oficiales de GHC) publicaron en 2026 hdb, el primer debugger DAP de Haskell que respeta la evaluacion lazy. No simula ser estricto. No hace trampas. Te deja inspeccionar thunks como thunks, ver "esta expresion ya esta evaluada, esta no, esta a medias", hacer step over en codigo perezoso con semantica correcta.

Y como habla DAP (el protocolo estandar), funciona en VSCode, Doom Emacs, IntelliJ con LSP4IJ, Vim, Helix… cualquier editor moderno que entienda DAP.

Para mi, esto es la noticia tecnica del año en el ecosistema. Pero la importancia no es tecnica. Es cultural y pedagogica.

Lo que pasa ahora cuando aprendes

Pascual va a abrir mañana Debugging101.hs y va a:

  1. Poner un breakpoint en let xs = primerosCincoParesAlCuadrado.
  2. Pulsar SPC d d o el escarabajo de IntelliJ.
  3. Ver en el panel de variables: primerosCincoParesAlCuadrado :: [Int] = _.
  4. Pulsar step over.
  5. Ver como ese guion bajo se transforma en [4, 16, 36, 64, 100].
  6. Pulsar step in dentro de take 5 $ map (^2) $ filter even [1..1000000].
  7. Ver como take 5 consume cinco elementos de map, como map los pide a filter, como filter recorre [1..1000000] solo hasta encontrar los cinco necesarios.

En quince minutos habra entendido lo que un libro le hubiera tomado tres capitulos. Porque en lugar de leer una descripcion de la evaluacion, va a observar la evaluacion. La maquina le va a mostrar lo que esta haciendo.

Y la siguiente vez que escriba codigo Haskell, su intuicion no sera "creo que esto es perezoso por que el libro lo decia", sino "esto es perezoso por que lo vi parar aqui y avanzar alla". Diferencia abismal.

Por que el debugger es mejor maestro que el libro

Tres razones, basadas en como funciona el cerebro humano:

1. Feedback inmediato

El cerebro aprende mejor cuando hay feedback rapido y especifico. Un libro te explica algo y luego, cinco paginas despues, te pone un ejercicio. Entre la explicacion y la verificacion pasan minutos u horas. El debugger es feedback en milisegundos: pulsas step, ves que pasa, ajustas tu modelo mental, vuelves a pulsar.

Es la misma razon por la que se aprende a tocar guitarra mas rapido con un afinador electronico que con el oido educado del profesor: te equivocas, lo ves, corriges. Veinte veces por minuto.

2. Hipotesis verificable

El debugger convierte tus dudas en preguntas testables. "Creo que xs aqui ya esta evaluado pero no se" se transforma en "voy a mirar en el panel". Aprendes a TENER dudas, formularlas, y contestarlas tu mismo. Eso es pensar como cientifico, lo que queremos que aprenda todo programador.

Sin debugger, las dudas se quedan en la cabeza, se acumulan, y a los pocos meses estas programando con un modelo mental que tiene quince errores no detectados. Y tu codigo "funciona" pero no sabes por que. Eso es ingenieria de chamanes.

3. Concrecion vs abstraccion

Los libros ensenan en abstracto: "los thunks son representaciones diferidas". Los debuggers ensenan en concreto: "este xs AQUI es <thunk> AHORA, y despues de F10 es [4,16,36,64,100]". El cerebro aprende abstracciones a partir de concretos repetidos, no al reves. Por eso los libros de Haskell te abruman al principio: te tiran abstracciones sin darte concretos en los que apoyarlas.

Lo que cambia para la comunidad

Tres consecuencias que llegaran inevitablemente:

Mas gente aprendera Haskell

La barrera de entrada acaba de bajar un orden de magnitud. Lo que antes te tomaba un mes de leer y dibujar, ahora te tomara una tarde de pulsar F10. Y la tarde es divertida, mientras que las pizarras son tediosas. La gente que abandonaba en el capitulo 4 va a llegar al 8.

Mas empresas adoptaran Haskell

"No usamos Haskell porque no tiene debugger" era una afirmacion ridicula en boca de gente que en Java o Python tiene tests y casi nunca usa el debugger. Pero era un argumento facil en reuniones de comite tecnico. Adios.

La identidad de la comunidad cambiara

Haskell era **"el lenguaje donde no hace falta debugger porque los tipos te lo evitan"**. Va a pasar a ser **"el lenguaje donde el debugger te ensena la diferencia entre lazy y strict, una cosa que los demas lenguajes ni saben que existe"**. Mas humilde, mas pragmatico, mas atractivo.

Y un comunidad que durante decadas se sintio "elite" por aguantar sin tooling moderno, va a tener que renegociar su narrativa. Espero que con elegancia, no con resentimiento.

El paralelo con otras revoluciones

Cuando llego el REPL interactivo (IPython, ghci, irb…), la gente mayor decia "yo aprendi sin REPL y se mas que tu". Pero los aprendices con REPL acabaron sabiendo mas. La diferencia: feedback inmediato.

Cuando llego hot reload en frontend (Vite, etc), los devs viejos decian "yo aprendi sin hot reload y soy mejor". Pero los nuevos escriben tres veces mas codigo en el mismo tiempo. La diferencia: feedback inmediato.

Cuando llegaron los DevTools del navegador (inspector de elementos, panel de Network), los del HTML manual decian "ahora cualquiera hace web". Y tenian razon: cualquiera hizo web, y la web crecio. La diferencia: feedback inmediato.

El debugger DAP de Haskell es a Haskell lo que los DevTools fueron a la web. Va a poner el lenguaje en manos de gente que antes no podia entrar.

Lo que esto significa para Pascual

Pascual es PHP dev de profesion. Lleva veinte anos escribiendo codigo. Sabe pensar, sabe modelar, sabe diseñar sistemas. Pero nunca habia podido cruzar el muro Haskell porque la lazy evaluation era invisible para su intuicion estricta.

Esta noche hemos puesto el debugger en sus dos editores. Mañana abrira foldrLazyDemo y vera, por primera vez en su vida, un thunk evaporarse en pantalla. Y la semana que viene, cuando intente escribir su propia funcion perezosa, no la imaginara: la verificara.

A partir de aqui, su Haskell sera un Haskell observable. Y un Haskell observable es un Haskell aprendible.

Cierre

Cuando un campo cambia de "aprender es heroico" a "aprender es posible", el campo se democratiza. Eso es lo que pasa con Haskell ahora. No es que vaya a hacerse popular de la noche a la mañana — los lenguajes funcionales tienen otros muros que el debugger no tira — pero acaba de quitarse el muro mas absurdo: el muro artificial creado por la falta de feedback visual.

El thunk que dibujabas en pizarra ahora esta en tu pantalla. La intuicion que se ganaba en dos años se gana en un mes. La comunidad que se sentia elite empieza a tener un puente para nuevos.

Pascual: mañana, cuando veas el primer thunk evaporarse, acuerdate que eso lo viste TU. No te lo dijeron, no lo memorizaste. Lo viste. A partir de ahi, eres uno mas en el club.

Bienvenido a aprender Haskell de verdad.

Comparte este post:

Es tu post

Estas seguro? Esto no se puede deshacer.

Comentarios (0)

Sin comentarios todavia. Se el primero!

Deja un comentario