{"slug": "quick-tips-for-fast-iteration-in-haskell", "title": "Quick tips for fast iteration in Haskell", "summary": "The Haskell community is adopting faster iteration techniques for type checking and compilation, driven by the rise of agentic coding. Tom Ellis and Laurent P. René de Cotret outline two methods: using GHCi's interactive interpreter for quick feedback and optimizing build flags for ghc and cabal, both applicable to existing codebases without restructuring.", "body_md": "[Quick tips for fast iteration in Haskell](https://blog.haskell.org/quick-tips-for-fast-iteration-in-haskell/)\n\n[Tom Ellis](https://blog.haskell.org/authors/tom-ellis/),\n\n[Laurent P. René de Cotret](https://blog.haskell.org/authors/laurent-p-rene-de-cotret/)July 28, 2026 [\n\n[Ecosystem](https://blog.haskell.org/categories/ecosystem/)] #\n\n[Practices](https://blog.haskell.org/tags/practices/)#\n\n[Tooling](https://blog.haskell.org/tags/tooling/)\n\n## Introduction\n\nThe Haskell community has recently been discussing how to achieve fast iteration times when type checking and compiling Haskell code. Fast feedback has always been important in software development, and we're seeing increased interest in the topic in recent months due to the rise in prominence of agentic coding.\n\nThis article describes two techniques for fast iteration: using `ghci`\n\nto get fast feedback on the result of type checking an compilation,\nand speeding up builds with a careful choice of flags to `ghc`\n\nand\n`cabal`\n\n. These techniques can be run on any existing codebase. They\ndon't require you to restructure your code in any way, so you can get\nan instant improvement to your iteration times. Let's dive in!\n\n`ghci`\n\n-based\n\nGHCi is [GHC's interactive\ninterpreter](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/ghci.html)\n(executable name `ghci`\n\n) which comes bundled with every installation\nof GHC. It is a REPL (\"[read-eval-print\nloop](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)\"),\nwhich means you can type expressions into it and it will run them,\nprinting the result. `ghci`\n\nis a good starting point for fast\nfeedback because it doesn't have to fully compile code, rather it\ngenerates executable bytecode and skips machine code generation,\nmaking it inherently faster than a normal build.\n\nThere are a variety of ways to use `ghci`\n\n, from using barebones `ghci`\n\nitself directly to featureful wrappers.\n\n`ghci`\n\nitself\n\nYou can invoke `ghci`\n\nwith a collection of source paths and it will\ncompile and load those modules, for example:\n\n```\nghci src/Path/Module1.hs src/Path/Module2.hs\n```\n\nOr, perhaps more useful, use a `ghci`\n\ninvocation wrapper that comes\nwith your build tool, `cabal`\n\nor `stack`\n\n:\n\n```\nstack ghci\ncabal repl\n```\n\nFor a simple `ghci`\n\nworkflow, make some changes to the files in your\nproject, then navigate to your `ghci`\n\nwindow and type `:reload`\n\n(or\n`:r`\n\nfor short). `ghci`\n\nwill type check and compiles your changes,\nprinting any errors or warnings from GHC. Simple!\n\nYou might feel that continually typing `:r`\n\nought to be automated\naway. If so then check out the next section on `ghcid`\n\n.\n\n`ghcid`\n\n[ ghcid](https://github.com/ndmitchell/ghcid) is a wrapper around\n\n`ghci`\n\nthat automates issuing `:r`\n\nwhen\nany file in your project changes, so its workflow is even easier than\nthat of `ghci`\n\n: make some changes to the files in your project and\nthen merely *look at*your\n\n`ghcid`\n\nwindow; `ghcid`\n\nwill have detected\nyour changes and the result of type check and compilation will appear\nautomatically.#### Automatically running tests\n\n`ghcid`\n\nalso allows you to get more quick feedback than just the\nresult of type checking and compilation: you can run tests (or indeed\nany code) when your source files change. There are two ways to do\nthis:\n\n-\nEmbed comments with expressions to be evaluated. For example add this comment to a source file to evaluate\n\n`expr`\n\nafter loading:\n\n```\n-- $> expr\n```\n\n(Evaluating embedded comment expressions requires using the\n\n`--allow-eval`\n\nflag.) -\nPass an expression to the command line option\n\n`--test`\n\n, to run after the code is loaded successfully, for example:\n\n```\nghcid --test myTestFun\n```\n\n(By default the test expression will only run if the code is warning-free. To run the test expression even if there are warnings, also pass\n\n`--warnings`\n\n.)\n\nFor more information on these features, see the\n[Evaluation](https://github.com/ndmitchell/ghcid#evaluation) section\nof the `ghcid`\n\nREADME.\n\n#### Installation\n\n`ghcid`\n\nis a normal executable package on Hackage, so you can install\nit with, for example, `cabal install ghcid`\n\n.\n\n`ghcid-check`\n\nOne limitation of `ghcid`\n\n's default behaviour is that it is not\n*programatic*. When you change source files, `ghcid`\n\n's updates appear\nin the terminal in which it is already running. That's no good if you\nwant to give fast feedback to a coding agent; ideally the coding agent\nwould run a command line executable to obtain the feedback.\n\nThat's fine, because `ghcid`\n\nsupports that too, with its `--reload`\n\nand\n`--outputfile`\n\noptions. But setting that up is a little fiddly, so\nhere's a simple `ghcid`\n\nwrapper `bash`\n\nscript called `ghcid-check`\n\n:\n\n`ghcid-check`\n\nis very basic and you might want to customize it to meet\nyour own particular needs.\n\nBeyond `ghcid`\n\n, there are another couple of projects based on a\nsimilar idea that bring new features: `ghciwatch`\n\nand Tricorder.\n\n`ghciwatch`\n\n[ ghciwatch](https://github.com/mercurytechnologies/ghciwatch#ghciwatch)\nis an updated take on\n\n`ghcid`\n\n, from the Haskell developers at\n[Mercury](https://mercury.com/blog/topics/engineering-blog). Compared to\n\n`ghcid`\n\nit has more sophisticated checking for new, removed and new\nmodules, which sometimes trip up `ghcid`\n\n, as well as other new\nfeatures.### Tricorder\n\n[Tricorder](https://github.com/tweag/tricorder#tricorder) is from\nTweag and provides even more live status and diagnostic updates than\n`ghcid`\n\nor `ghciwatch`\n\n, including access to documentation and\nmachine-readable output, and is designed from the ground up to be\nusable programatically by coding agents as well as interactively by\nhumans.\n\nCheck out the [announcement\npost](https://discourse.haskell.org/t/ann-tricorder-a-new-development-tool-for-haskell-and-llms/14208)\non the Haskell Discourse, which includes a short video of Tricorder\nfunctionality. `tricorder`\n\nis a normal executable package on Hackage,\nso you can install it with, for example, `cabal install tricorder`\n\n`cabal`\n\n-based\n\nLet's now see how to speed up development builds using the [ cabal](https://www.haskell.org/cabal/) build system.\n\n`cabal`\n\nhas existed for a very long time, and you may be surprised by some of the things it offers!Speeding up development builds using `cabal`\n\ninvolves three broad optimizations:\n\n- Tuning GHC's runtime options;\n- Decreasing the amount of work\n`cabal`\n\nand GHC have to do by disabling compile-time optimizations; - Increasing resource-sharing for more effective build parallelism.\n\nBefore we describe the build optimizations above, it is worth understanding how cabal can be configured. Cabal uses *project* files to bundle configuration options related to multiple packages. The default configuration file, which is picked up automatically by `cabal`\n\n, is `cabal.project`\n\n. A typical `cabal.project`\n\nfile for a project with three packages looks like:\n\n```\npackages:\n    packageA\n    packageB\n    packageC\n```\n\nRecent versions of cabal allow project files to import other projects files. Therefore, without changing our default `cabal.project`\n\nfile, we'll create a new configuration file specifically for development builds. Let's call it `cabal.fast.project`\n\n:\n\n```\nimport: cabal.project\n```\n\nAs we go through build optimizations below, we'll append to our `cabal.fast.project`\n\n. Then, a fast build will specify to use `cabal.fast.project`\n\n:\n\n``` bash\n$ cabal build all --project-file=cabal.fast.project\n```\n\nThat way, we have an easy set-up for fast builds. We could have project files for profiling builds, release builds, etc.\n\nRunning the above command, we got a baseline compilation time on a private, commercial Haskell codebase of more than 150 000 lines of Haskell code. As we go through build optimizations below, we will report on the speedups achieved from this baseline.\n\n### Tuning GHC's runtime options\n\nGHC is a wonderful piece of technology. In some ways, it might as well be from the future. However, compiling Haskell programs into performant executables is hard work, and GHC is itself a Haskell program. This means we must tune its runtime system to squeeze out more performance.\n\nTake a look at the [GHC 9.14.1 user guide's section on runtime control](https://downloads.haskell.org/ghc/9.14.1/docs/users_guide/runtime_control.html). In our experience, however, simply tuning the garbage collection allocation size is a great first step if your computer is somewhat recent. In particular, increasing the allocation size from the default of 4MB to 64MB, 128MB, er even 256MB may dramatically increase compilation throughput!\n\nThus, we update our `cabal.fast.project`\n\nproject file to pass the appropriate flag to GHC. We'll use an allocation area of 64MB, but do experiment on your own workloads:\n\n``` python\nimport cabal.project\n\n-- Step 1: tuning GHC\nprogram-options:\n   ghc-options: +RTS -A64m -RTS\n```\n\nBuilding with the above project file, we achieve a speedup of approximately 10%. Not huge, but not marginal either. Big gains remain!\n\n### Disabling compile-time optimizations\n\nEven if GHC is given the appropriate resources to do its job more effectively, it still does a lot of work by default, since GHC's default optimization level is `-O1`\n\n. This means that GHC performs *some* optimizations, which necessarily requires more compilation time.\n\nBut this is merely the *default* behavior, and this article is all about speed. Instead, we can disable optimizations via the `optimization`\n\noption in cabal project files:\n\n``` python\nimport cabal.project\n\n-- Step 1: tuning GHC\nprogram-options:\n   ghc-options: +RTS -A64m -RTS\n\n-- Step 2: disable optimizations\noptimization: false\n```\n\nNote that this optimization flag only applies to your locally-built packages. Your third-party dependencies will be compiled using the default optimization level, `-O1`\n\n. This is generally considered acceptable since your third-party dependencies are rarely built, and it allows you to re-use them for other build profiles.\n\nAt this stage, the codebase is building 2x faster!\n\n### Better resource-sharing for build parallelism\n\nWe've tuned GHC, and minimized the amount of work it needs to do. What is left is speed up the build across *multiple packages*.\n\n`cabal`\n\nhas an option for parallel builds called `--jobs`\n\n. You can either specify a number of capabilities to dedicate to the build (e.g. `--jobs=2`\n\n), or let `cabal`\n\ndecide based on your hardware (using a bare `--jobs`\n\n). Higher is not *always* better; this depends on your build graph!\n\nIn our experience, a number from 4 - 8 is appropriate to start with. We therefore update our `cabal.fast.project`\n\nproject file:\n\n``` python\nimport cabal.project\n\n-- Step 1: tuning GHC\nprogram-options:\n   ghc-options: +RTS -A64m -RTS\n\n-- Step 2: disable optimizations\noptimization: false\n\n-- Step 3: build parallelism\njobs: 6\n```\n\nWe can do even better! If you use `cabal`\n\n3.12+ in combination with GHC 9.8+, you can take advantage of a new flag, `--semaphore`\n\n, which allows `cabal`\n\nto share resources better with GHC. While [Well-Typed has a full breakdown of the why and how](https://well-typed.com/blog/2023/08/reducing-haskell-parallel-build-times/), we summarize the result here. In short, if cabal can take advantage of multiple parallel invocations of GHC, it will do so; however, under the `--semaphore`\n\noption, if cabal *cannot* take advantage of all the cores it was allocated, it can tell GHC to use more cores. This can result in compilation time reductions of up to 30%!\n\nWe update our `cabal.fast.project`\n\none final time:\n\n``` python\nimport cabal.project\n\n-- Step 1: tuning GHC\nprogram-options:\n   ghc-options: +RTS -A64m -RTS\n\n-- Step 2: disable optimizations\noptimization: false\n\n-- Step 3: build parallelism\njobs: 6\nsemaphore: True\n```\n\nFinally, our builds will be much faster:\n\n``` bash\n$ cabal build all --project-file=cabal.fast.project\n```\n\nCompared to our baseline, we achieve a cumulative build speedup of 2.6x!\n\n## Going further\n\nIf you're eager for even more build time improvements and are willing\nto spend time adjusting the structure of your codebase, we can point\nyou where to look next: watch of Teo Camarasu's talk at the 2026\nHaskell Ecosystem Workshop, [Optimising for fast builds with\nGHC](https://www.youtube.com/watch?v=nmE6aa_I5TU).\n\n## Conclusion\n\nWe hope you enjoyed our brief tour of using `ghci`\n\nand `cabal`\n\nfor\nfast iterative development in Haskell. If you have any comments or\nquestions, or have your own tips and techniques you can share with the\ncommunity, then please post on [the Haskell\nDiscourse](https://discourse.haskell.org/).\n\n## References\n\nLots of people have already written up their experience of workflows\nthat `ghci`\n\n, including for fast iterative development. You might want\nto check them out.\n\n[Using GHCi — GHC Users Guide](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/ghci.html)[Fast Feedback](https://haskellweekly.news/episode/6.html)[Haskell Development Workflows (4 ways)](https://academy.fpblock.com/blog/2018/08/haskell-development-workflows-4-ways/)[ghcid for the win!](https://www.parsonsmatt.org/2018/05/19/ghcid_for_the_win.html)[ghcid for Web App Development](https://functor.tokyo/blog/2019-04-07-ghcid-for-web-app-dev)[Announcing ghciwatch 1.0](https://mercury.com/blog/announcing-ghciwatch)[Haskell dev workflow with ghcid and neovim](https://jeancharles.quillet.org/posts/2024-09-04-Haskell-dev-workflow-with-ghcid-and-neovim.html)[Multiple Component support for cabal repl](https://www.well-typed.com/blog/2023/03/cabal-multi-unit/)[Cheaper: producing a program with less developer time](https://github.com/alexfmpe/semantic-satiation/blob/main/posts/002-cheaper.md)[Rapid](https://hackage.haskell.org/package/rapid/docs/Rapid.html)", "url": "https://wpnews.pro/news/quick-tips-for-fast-iteration-in-haskell", "canonical_source": "https://blog.haskell.org/quick-tips-for-fast-iteration-in-haskell/", "published_at": "2026-07-28 19:31:33+00:00", "updated_at": "2026-07-28 19:52:25.785849+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Tom Ellis", "Laurent P. René de Cotret", "GHCi", "GHC", "cabal", "stack", "ghcid"], "alternates": {"html": "https://wpnews.pro/news/quick-tips-for-fast-iteration-in-haskell", "markdown": "https://wpnews.pro/news/quick-tips-for-fast-iteration-in-haskell.md", "text": "https://wpnews.pro/news/quick-tips-for-fast-iteration-in-haskell.txt", "jsonld": "https://wpnews.pro/news/quick-tips-for-fast-iteration-in-haskell.jsonld"}}