The fastest way to feed ghc type errors to LLM

I see couple of Haskellers around here embracing agentic coding especially since the jump of capabilities we got in claude 4.5/6 releases. I’ve also been experimenting with claude-code recently and saw some sparks of great productivity improvements.

When using claude-code, there are at least two downsides of letting it run cabal/stack build on an ad-hoc basis:

  • the feedback loop is slow (compared to what you can get with ghcid / ghciwatch or just reloading things in ghci when coding manually)

  • it’s wasting tokens (if you’re compiling 200 module project, you’ll see 200+ lines of build progress logs [ 17 of 226] Compiling PI.Core.ProjectPermission even if the project compiles fine, the repeated build outputs just pollute the context)

I see some hope in claude hooks, which allow you to run arbitrary scripts/commands in response to events like claude writing/editing a file. When a hook command is executed in response to an event, it either exits with 0 (in which case the agentic harness doesn’t bother the LLM with the output of that command), or it exits with 2 and the stderr of the script (think type errors) is fed back to claude so it can react to it. I’ve noticed a great UX improvement in my agentic coding sessions when using hooks with my toy elm projects. Here’s an example hook that just runs elm compiler every time elm file is written or edited. It works great, in effect turning “work on something until Claude thinks it’s done” loops into more productive “work on something until the compiler agrees with Claude it’s done” loops. You just instruct claude in CLAUDE.md not to run any build commands explicitly and assure it it will get GHC compiler errors from the hook if anything gets broken. This works awesome with Elm as it can compile 100k-line project in under 2 seconds.

Is there a way to get fast type-check-only feedback from GHC/cabal/stack (similar to rust’s cargo check) for use in an automated hook?

  • stack build --fast / cabal build --disable-optimization are usable, but still slow (unnecessary code gen and linking steps). It seems like it’s not possible to use -no-code with cabal build (thread)

  • ghciwatch is great for manual coding, but I haven’t found a way to execute it in non-interactive “one off” compilation way or how to get its output in an “on demand” way

I’m curious if anyone managed to figure out something to get fast feedback loop for their LLM agents.

6 Likes

Asked myself the same question. Ideally Claude would connect to HLS. (This isn’t even Haskell specific, connection to any language server would be beneficial.) Since this idea is not so far fetched I’m assuming that there is a solution out there already, but I couldn’t find it yesterday.

I think CC has native LSP support

1 Like

Use ghcid or ghciwatch.

You can pipe the output to a file using --outputfile compilation-errors.log. It will only give you errors and warnings, no noise. Presumably you can prompt the LLM to monitor the file instead of running a full build.

Edit: I just tried this with codex and it seems to work nicely. I added a devcheck script which checks if ghcid is running with the appropriate flag:

devcheck.exec = ''
   if pgrep -af "ghcid.*--outputfile .compilation-errors" >/dev/null; then
     echo "dev is running (ghcid writing to .compilation-errors):"
     pgrep -af "ghcid.*--outputfile .compilation-errors"
   else
     echo "dev is not running"
     exit 1
   fi
 '';

Then prompted the LLM to run devcheck and if it’s successful, monitor the .compilation-errors file.

8 Likes

I couldn’t find any concrete resources on how to wire e.g. Github Copilot (which itself connects to Claude) with HLS. There are some resources on how to connect it to an MCP, so maybe HLS needs an MCP server?

This is what I do, and it works well unless the agent needs to reload ghcid to switch packages or add/remove files from the current package. I find that it gets confused in such cases and I’ll point it at a worktree instead, and just let it run cabal build or whatever, trading off a bit of raw speed for less oversight.

1 Like

Apologies, Github Copilot does connect to HLS automatically (it just sometimes needs to be reminded of this fact), so it receives the type errors immediately. This is the fastest way I know so far.

Correct, if you have hls running in VSCode, you just need to give the agent the permission to read the problems panel (which is not enabled by default I believe) and it’s able to take the errors appearing there into account. This is great if you have hls + vscode setup. I was looking for something more lighweight which I can run on demand without having to run some persistent process in the background and manage its state.

2 Likes

I apologize in advance and this definitely doesn’t answer your question in regards to Haskell but I just wanted to point out this repo I found yesterday for a new AI specific language that builds LEAN proofs for each change. Seems to be exactly the type of thing that can keep these hallucination machines on a set of rigid guardrails: GitHub - jasisz/aver: Aver is a programming language for auditable AI-written code: verify in source, deploy with Rust, prove with Lean/Dafny · GitHub

Seems like the absolute best way to truly get the most out of each token in the LLM.

Since it builds Rust, perhaps it could also build Haskell Core? Anyway, it’s an interesting concept that materializes some of the theories about best practices that I’ve pondered upon noticing how much better LLM’s are when they have a rich type system like Haskell’s for feedback.

I believe Claude Code requires a “Code intelligence” plugin to serve as intermediary. I wonder if there’s one for HLS.

Edit: I have published a Claude plugins marketplace in GitHub with an example haskell-lsp plugin.

3 Likes

I’ve been using GitHub - m4dc4p/claude-hls · GitHub . The only downside is that the diagnostics are often stale, presumably due to asynchronicity, but I submitted an issue on Github and already see activity around it! Apart from that one thing, it’s been really great. Thanks, @m4dc4p!

1 Like

I’ve been using OpenCode, which has built-in LSP support for diagnostics, with HLS among the servers that are preconfigured. It also has experimental support for using other LSP features, like querying docs or inferred types.

1 Like

Another way of feeding errors to Claude Code is through the /ide command and the extension for VSCode. It contains a mcp__ide__getDiagnostics tool that reads VSCode’s problems panel:

❯ /ide
  ⎿  Connected to Visual Studio Code.

❯ What errors are there?

● ide - getDiagnostics (MCP)
...
2 Likes