Howto: debugger Haskell (hdb) en Doom Emacs paso a paso


5 de mayo de 2026

Para quien es esto

Quieres pausar codigo Haskell en Doom Emacs, ver tus thunks expandirse en vivo en el panel de variables, y hacer step over en una expresion perezosa. Esta guia te lleva del cero a tu primer breakpoint.

Si quieres entender POR QUE esto importa o leer el diario de batalla con todas las trampas, mira mi otro post: Hito doble: Haskell con debugger en Doom y en IntelliJ.

Aqui solo pasos.

Requisitos

Paso 1: GHC 9.14 disponible en el proyecto

hdb requiere GHC 9.14 o superior. En tu flake.nix:

{
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
  outputs = { nixpkgs, ... }:
    let
      pkgs = import nixpkgs { system = "x86_64-linux"; };
    in {
      devShells.x86_64-linux.default = pkgs.mkShell {
        buildInputs = [
          pkgs.haskell.compiler.ghc914
          pkgs.cabal-install
        ];
      };
    };
}

Verifica entrando al shell:

cd ~/src/tu-proyecto-haskell
nix develop
ghc --version   # debe mostrar 9.14.x

Paso 2: Instalar hdb (haskell-debugger)

Dentro del nix develop:

cabal update
cabal install haskell-debugger --installdir=$HOME/.local/bin

Esto compila hdb y deja el binario en ~/.local/bin/hdb. Tarda unos minutos la primera vez.

Verifica:

which hdb              # /home/tu-usuario/.local/bin/hdb
hdb --version

Paso 3: Wrapper hdb-dap

Cuando un editor (Doom, IntelliJ) lanza hdb, no hereda el shell de nix develop. Sin GHC 9.14 en PATH, hdb aborta. El wrapper resuelve esto entrando en nix develop antes de ejecutar.

Crea ~/.local/bin/hdb-dap:

#!/usr/bin/env bash
set -euo pipefail

# Sube buscando flake.nix
find_flake_root() {
  local dir
  dir="$(pwd)"
  while [ "$dir" != "/" ]; do
    if [ -f "$dir/flake.nix" ]; then
      echo "$dir"; return 0
    fi
    dir="$(dirname "$dir")"
  done
  return 1
}

flake_root="$(find_flake_root || true)"
if [ -z "$flake_root" ]; then
  echo "[hdb-dap] no flake.nix encontrado desde $(pwd)" >&2
  exit 1
fi

cd "$flake_root"

# Ruta absoluta a hdb. nix develop NO incluye ~/.local/bin en PATH.
HDB_BIN="${HDB_BIN:-$HOME/.local/bin/hdb}"
if [ ! -x "$HDB_BIN" ]; then
  echo "[hdb-dap] $HDB_BIN no encontrado" >&2
  exit 1
fi

exec nix develop --command "$HDB_BIN" "$@"

Hazlo ejecutable:

chmod +x ~/.local/bin/hdb-dap

Test:

cd ~/src/tu-proyecto-haskell
hdb-dap server --port 19999 &
sleep 25                      # primer arranque tarda
ss -tlnp | grep 19999         # debe estar listening
kill %1

Paso 4: hie.yaml

Dile a hdb que tu fichero pertenece al target del proyecto. Crea o edita hie.yaml en la raiz:

cradle:
  cabal:
    - path: "./src"
      component: "lib:tu-proyecto"

    - path: "./app/Main.hs"
      component: "tu-proyecto:exe:tu-exe"

Adapta los componentes a tu .cabal. Si no tienes hie.yaml, hdb falla con "Multi Cradle: No prefixes matched".

Paso 5: Configurar dape en Doom

Doom no trae dape por defecto. Edita ~/.config/doom/packages.el:

(package! dape)

Edita ~/.config/doom/config.el y añade:

(after! flycheck
  (add-to-list 'flycheck-disabled-checkers 'haskell-stack-ghc))

(use-package! dape
  :config
  ;; hdb tarda ~20s descubriendo flags el primer launch
  (setq dape-request-timeout 60)

  (add-to-list 'dape-configs
               `(haskell-hdb
                 modes (haskell-mode haskell-ts-mode haskell-cabal-mode)
                 ensure (lambda (config)
                          (unless (executable-find "hdb-dap")
                            (user-error "hdb-dap no encontrado en PATH")))
                 fn (dape-config-autoport)
                 host "localhost"
                 port :autoport
                 command "hdb-dap"
                 command-args ("server" "--port" :autoport)
                 :type "haskell-debugger"
                 :request "launch"
                 :projectRoot dape-cwd-fn
                 :entryFile dape-buffer-default
                 :entryPoint "main"
                 :entryArgs []
                 :extraGhcArgs []
                 :internalInterpreter t)))

Ejecuta doom sync y reinicia Emacs.

Paso 6: Tu primer breakpoint

  1. Abre cualquier .hs dentro del proyecto Haskell.
  2. Verifica que haskell-mode esta activo (mode-line lo muestra).
  3. Coloca el cursor en una linea con expresion (un let, un do, una llamada a funcion). Pulsa F9 para poner breakpoint. Sale bola roja en el margen.
  4. Pulsa espacio-d-d (SPC d d, comando dape) y selecciona haskell-hdb en el menu.
  5. Espera 15-25 segundos. hdb compila el modulo dentro del flake y conecta.
  6. Cuando para en el breakpoint, abre el panel de variables con SPC d v. Veras los valores locales y los modulos.

Paso 7: Ver un thunk en vivo

En el panel de variables, busca una expresion que sepas que es perezosa. Por ejemplo, si tienes:

main = do
  let xs = take 5 (map (^2) (filter even [1..1000000]))
  print xs

Pon breakpoint en la linea let xs = .... Cuando para, en el panel veras xs como <thunk> o con un guion bajo (depende del bridge DAP de tu version).

Pulsa F10 (step over). Cuando llegue al print xs, esa linea fuerza la evaluacion. Vuelve a inspeccionar xs y ahora sera [4,16,36,64,100]. Acabas de ver lazy evaluation en vivo.

Atajos de teclado utiles

Atajo Comando dape Que hace
F5 dape-restart Reinicia la sesion
F9 dape-breakpoint-toggle Pon/quita breakpoint
F10 dape-next Step over (siguiente linea)
F11 dape-step-in Step in (entra en funcion)
F12 dape-step-out Step out (sale a la llamada)
SPC d d dape Arranca debugger
SPC d k dape-disconnect Cierra sesion
SPC d v dape-info Panel info (Variables)
SPC d e dape-evaluate-expression Evaluar expresion

Lo que no esperar

Cierre

Con esto ya puedes pausar codigo Haskell, inspeccionar thunks y hacer stepping. La intuicion de lazy evaluation que antes solo se ganaba leyendo libros, ahora se ve en pantalla.

El howto equivalente para IntelliJ Ultimate esta aqui: Howto IntelliJ + LSP4IJ + hdb.

Comparte este post:

Es tu post

Estas seguro? Esto no se puede deshacer.

Comentarios (0)

Sin comentarios todavia. Se el primero!

Deja un comentario