This is the thirty-second edition of our Haskell ecosystem activities report, which describes the work Well-Typed are doing on GHC, Cabal, HLS and other parts of the core Haskell toolchain. The current edition covers roughly the months of June, July and August 2026.

You can find the previous editions collected under the haskell-ecosystem-report tag.

Sponsorship

We offer Haskell Ecosystem Support Packages to provide commercial users with support from Well-Typed’s experts while investing in the Haskell community and its technical ecosystem including through the work described in this report. To find out more, read our announcement of these packages in partnership with the Haskell Foundation. We need funding to continue this essential maintenance work!

Many thanks to our Haskell Ecosystem Supporters: Standard Chartered, Channable and QBayLogic, as well as to our other clients who also contribute to making this work possible: Anduril, Juspay and Mercury; and to the HLS Open Collective for supporting HLS release management.

Highlights

Known-key names and reinstallable base

We have long been working to decouple GHC from base, to make it easier to upgrade the compiler independently from the (standard) libraries. A key restriction that prevented disentangling base from GHC was GHC’s treatment of known entities. These are things in the standard libraries that GHC needs to know something about (e.g. Show for deriving, Monad for desugaring do notation, etc). This is detailed in section 3.3 of a recent blog post by Simon Peyton Jones.

Rodrigo and Sam recently overhauled GHC’s treatment of known entities, in collaboration with Simon (!15899), finally landing the patch in August. This allowed known entities to be defined completely outside of the compiler for the first time, without hard-coding the precise module that defines them.

As part of this change, it was also necessary to rework some implementation details of Typeable, namely the KindRep datatype. Sam opened CLC proposal #433 to get approval on this change, which required an accompanying change to the binary package, which Rodrigo implemented in #244.

AArch64 code generation and sub-word handling

Andreas has been hunting down a slew of correctness bugs in the AArch64 native code generator, mostly related to the handling of sub-word values. Issues include sub-word handling across C calls (#27430), in atomic loads/stores (#27533), in conversions (#27537), in if conditionals (#27538), in %i2f64 (#27539) and the use of invalid immediates (#27550). These were all fixed in !16255, with a lot of the testing infrastructure being provided by Sam’s overhaul of test-primops (!43).

Coercion zapping

Extremely poor compiler performance for programs involving significant type-level computation has long plagued the community (#8095, recently re-reported as #27336). One major cause for this is blow-ups in coercion sizes due to the representation of accumulated type family reduction steps in Core, GHC’s typed intermediate language.

This problem resisted many attempts over the years, with promising approaches being brought down by performance regressions on certain types of programs (e.g. singleton-heavy programs). However, the future is bright for type-level wizards: MR !16378 implementing coercion zapping promises to bring the story to a satisfying conclusion.

This MR has an unusual history. In July, Sylvain Henry put up a proof-of-concept patch that was generated using Claude Fable. This patch was a carefully balanced “best of” previous approaches over the years, combining elements from MRs by Ben (!611), Adam (!6476), Sam (!7787) and Richard Eisenberg (!7909), each of which made significant progress on some programs but struggled to overcome significant compiler performance regressions on other programs.

Sam took over this patch and rewrote it from the ground up, doing a deep dive into GHC’s type family rewriter, and finding several related optimisation opportunities (e.g. !16479).

The net result is a patch that significantly improves compilation speed of type-family-heavy programs without regressing elsewhere. This patch is under final review and we hope to land it soon. Performance results from GHC’s testsuite:

Test case Compiler allocations Compilation time
T8095   768 MB ⇝   128 MB (−83%) 2.432s ⇝ 0.160s (−93%)
T9872a 1 656 MB ⇝ 1 406 MB (−15%) 0.971s ⇝ 0.494s (−49%)
T12545   698 MB ⇝   425 MB (−39%) 0.648s ⇝ 0.387s (−40%)
T13386   428 MB ⇝    35 MB (−92%) 0.259s ⇝ 0.104s (−60%)
T27336   499 MB ⇝    36 MB (−93%) 1.188s ⇝ 0.110s (−91%)
CoOpt_Singletons   634 MB ⇝   596 MB ( −6%) 0.484s ⇝ 0.360s (−26%)
LargeRecord 1 227 MB ⇝   967 MB (−21%) 0.872s ⇝ 0.617s (−29%)
FamAppCachePerf   496 MB ⇝   192 MB (−61%) 0.675s ⇝ 0.191s (−72%)

Exception annotations

Ben’s GHC proposal #330 and accompanying CLC proposal #164 proposed a deep rework of exception backtraces, providing several different backtrace mechanisms (e.g. HasCallStack, profiling cost centres, IPE).

However, the implementation of these proposals remained patchy, as detailed in Edsko’s recent blog post.

The team made several follow-up improvements to mechanisms for attaching information to exceptions:

  • Ben fixed WhileHandling dropping the ExceptionContext of the exception it is handling (!16275, #27456).
  • Sam made sure SomeException does not drop the ExceptionContext of the exception it is storing (!16274, #27455), with accompanying CLC proposal #432.
  • Wolfgang added rethrowSTM (!16501), incorporated the re-export into the stm submodule (!16616), and upstreamed it to stm (#100), with accompanying CLC proposal #436.
  • Zubin made annotateSTM use catchSTM# rather than catch# (!16508), with accompanying CLC proposal #437.

Trees That Grow

Over a long period, GHC has been gradually migrating to the Trees That Grow representation for its internal AST datatypes, and improving the modularity of the AST within the GHC codebase (#19218, #21592). Once complete, this will make it possible to expose the AST in a package that does not depend on the rest of the ghc library, and unblocks doing the same for the parser. As well as improving modularity, this will make it easier to use in tools that parse Haskell syntax, and may unlock future simplifications to template-haskell.

Alex and Rodrigo have been working on the next steps in that process, reorganising the codebase so that there will be no modules in the Language.Haskell.* namespace depending on modules in the GHC.* namespace. This included decoupling Language.Haskell.Syntax from GHC.Hs.Doc (!15601) and from GHC.Types.SourceText (!15573), representing string literals in the AST with Text rather than FastString (#21628, !12846), and decoupling ModuleNames used in the AST from those in GHC (!16546). As part of the original migration strategy, various orphan instances were introduced, which Alex has been cleaning up (#27755, !16299, !16610).

GitLab infrastructure work

Keeping Haskell’s infrastructure available has been increasingly challenging over the past couple of months due to the continued rise in LLM-driven crawling. See for comparison the Creepy crawlies blog post by Konstantin Ryabitsev, detailing the challenges faced by the git hosting infrastructure for the Linux kernel.

The GHC GitLab instance relies on Anubis to reduce crawler traffic. In early August, Magnus traced a fresh surge in load to a crawler that was bypassing Anubis entirely. Blocking it cut the load on the GitLab instance dramatically.

Other important infrastructure items:

  • Magnus and Ben upgraded the instance’s PostgreSQL database, after which Ben carried the GitLab upgrade itself through to the latest release in a single fourteen-hour session. Besides clearing a long-standing backlog item, this made it possible to prioritise traffic from logged-in users.

  • Magnus upgraded the host running the GitLab container, and applied patches to the GitLab instance (e.g. to close certain security vulnerabilities).

GHC

Releases

Frontend

  • Magnus renamed ZonkAny to UnusedType and added pretty-printing logic for it (!16212, #27390), so that it no longer leaks confusingly in HLS output.

  • Magnus refactored the error reporting code for ExplicitLevelImports (!16195).

  • Adam marked various language extension flags as deprecated (!16145), implementing part of the categorisation of language extensions (#27329).

  • Sam relaxed representation-polymorphism restrictions on newtype constructor patterns (!16342, #20363).

  • Sam fixed a couple of issues with mis-oriented coercions produced by GHC (!16474, #27639, !16207, #27374).

  • Sam fixed a crash in the Data.Data instance for HsCtxt (!16142, #27359).

  • Sam reviewed a patch adding explanations for unsolved Typeable constraints (!16351).

Core & simplifier

  • Andreas stopped the specialiser looping on recursive dictionaries in interestingDict (!16559, #27705).

  • Zubin landed various fixes for “Entered absent arg” bugs (!16519, #27627, !16503, #27626), along with fixes for the interaction of absent fillers with speculative evaluation of dictionaries (!16219), following an extensive investigation of the crash reported in #25924.

  • Following on from Simon Peyton Jones’ earlier refactor of GHC.Core.Utils.mkTick (!15494), which Sam helped with, Sam and Magnus tied up several loose ends:

    • preserving tick ordering in tickTickedExpr (!16601), fixing a regression in which source notes were moved past each other;

    • allowing more ticks in argument position in rebuild_app (!16558);

    • stopping ticks being dropped around variables of type IO () (!16123), fixing #27182 and #27225;

    • stopping mkTick in Core Prep from breaking ANF (!16198).

Driver

  • Rodrigo made some improvements to the structure and caching of downsweep (!16330, #27053, #27461).

    During this work, Rodrigo and Sam found an opportunity to clean up some legacy Backpack logic, establishing that implicitRequirementsShallow can never reach a HoleUnit (!16346).

  • Rodrigo deduplicated and shared Modules in interface files (!16216, !16213, #27401).

  • Hannes shared UnitInfos across the UnitEnv using a UnitIndex and an external unit database cache (!16115), and arranged for the UnitIndexCache to be reused after initialising multiple home units (!16598, #27748).

  • Hannes added the backendInfoTableMapValidity backend predicate (!16234).

  • Hannes fixed the order of PackageDBFlags for the interactive home unit (!16591, #27640).

  • Hannes diagnosed the quadratic behaviour of ghc-pkg list (#27275).

  • Zubin carried out a substantial overhaul of the loader (!16493, !16582, !16583) to allow more control over unloading stale linkables, which is useful for HLS to efficiently evaluate expressions as files change in the IDE (#27606).

  • Zubin made UniqDFM.alter preserve insertion order (!16385).

  • Wolfgang finished up a patch sorting Usages by fingerprint, to ensure determinism (!15484).

Code generation

  • Andreas fixed common block elimination silently dropping a procedure’s info table (!16592, #27722).

GHCi and bytecode

  • Wolfgang added --show-byte-code, the counterpart of --show-iface for bytecode files (!16386, #26909).

RTS

  • Duncan continued his sustained work to overhaul RTS I/O managers and the scheduler. One of the main goals is to bring the non-threaded RTS in line with the threaded one (e.g. #27335), and to improve maintainability across I/O manager implementations. As part of this effort, Duncan landed a collection of I/O manager refactors (!16217), preparing the ground for fixing idle GC and deadlock detection on the non-threaded RTS. Duncan also brought the POSIX and Win32 ticker implementations more in line (!16023).

  • Andreas fixed pointer arithmetic in the RTS linker’s flushInstructionCacheRISCV64 (!16419, #27569).

Build system

  • Sam made Hadrian check for cabal-install >= 3.12 upfront (!16132, #27317), rather than failing obscurely later on.

  • David fixed the ghc-internal .def file name (!16190), removed unused wrapper scripts from the Windows bindist (!16138), and made Hadrian avoid response files when the command line is short enough (!16134), as response files were impeding developer workflows (#27230).

  • David stopped ghc-toolchain throwing an exception when candidate executables are not found (!16173, #27369).

Testsuite

  • Sam made the test driver normalise call-stack line numbers when referring to locations in libraries (!16247, #27387), so that tests are no longer sensitive to irrelevant internal changes.

  • Andreas made the process009 test use SIGKILL (!16429).

  • Zubin made various improvements to the JUnit output, most importantly showing diffs in the GitLab summary (!16309) and reporting fragile failures as skipped (!16250). He also landed assorted testsuite fixes (!16413).

CI and infrastructure

  • Magnus worked on keeping nightly CI passing (!16296, #27127), adding a retry when fetching test metrics (!16281).

  • David made the lint-changelog job check for the no-changelog label in the script rather than in rules (!16177), which avoids having to re-run the entire pipeline when applying the no-changelog label.

  • Zubin arranged for tool messages to be reported as errors or warnings depending on the tool’s exit code, rather than always as errors (!16170, #27370).

process library

  • Zubin fixed link errors on certain macOS configurations by allowing posix_spawn_file_actions_addchdir to be missing when linking against process on Darwin (#377, #376).

Cabal

Releases

Recompilation checking

  • Sam stopped Cabal from writing setup-config twice (#11949), fixing access-denied failures (#11619).

  • Sam made cabal-install fall back to the external setup method on a Cabal-library mismatch (#11950), fixing stuck rebuilds of build-type: Custom packages (#11942).

  • Sam allowed preserving the local build artifacts across minor Cabal version bumps without causing recompilation or errors (#12289).

  • Tobias switched pre-processor recompilation checks to use file monitoring (#12021, with a preparatory refactoring in #12065). This fixes an issue with build tools not being re-run when their version changes (#11411).

Haskell Language Server

  • Andrea added whole-project loading (#5002), as described in his recent blog post. This builds on the groundwork Andrea laid in hie-bios: new load modes for upfront loading of a cradle or project (#508) and the componentsToLoad field (#515), both reviewed and merged by Hannes.

  • Hannes and Zubin optimised module-to-filename resolution (#4600).

  • Zubin made HLS unload linkables when any transitive dependency changes (#5035), and stopped HLS unloading all bytecode whenever new bytecode is generated (#5033).

  • Hannes set the FileCache hook before typechecking (#5030), fixing #4675.

  • Hannes ensured the DocMap is never computed with stale data (#4936).

  • Hannes tracked down multi-component session failures when expanding response files (#5004), fixed by whole-project loading.

  • Hannes substantially improved HLS’s error messages (#5029).

Google Summer of Code projects

Haskell syntax highlighting

  • Hannes transferred the VS Code syntax highlighting extension to the Haskell community (#256), fixing a mishap that had caused the extension to disappear from the VS Code marketplace, breaking syntax highlighting for many users.

Runtime observability

Haskell Debugger

Rodrigo continued to lead development of the Haskell Debugger (hdb), an interactive step-through debugger for Haskell, with contributions from Andrea and Hannes. It also gained a logo, designed by George Thomas (#326).

  • Hannes and Rodrigo added support for the future release of GHC 10.2 (#332, #359).

  • hdb has two mechanisms for listing threads, decoding stacks and collecting exception information: external compiler commands where GHC supports them, and a fallback that inspects the heap directly on older GHCs. Rodrigo separated the two into distinct modules selected by a Cabal version conditional, rather than interleaving them (#356).

  • Rodrigo made the singleThread flag of a continue request optional, as the Debug Adapter Protocol specifies (#38).

  • Hannes made the stack frame decoding logic use the source locations that StackAnnotation carries from ghc-internal-9.1402 onwards (#333).

  • Andrea made each stack frame report the variables bound in its own scope (#355, #160).

Live monitoring

Wen continued to develop eventlog-live, which allows GHC’s eventlog to be monitored as a program runs, streaming telemetry data from the runtime system to an external observability platform. One focus was on improving the OpenTelemetry integration.

Releases 0.6.0.0 through 0.9.0.0 came out this quarter. See the eventlog-live changelog for more details.

Stack and IPE profiling

Hannes and Wen worked on packages supporting stack profiling functionality, with a release of ghc-stack-profiler 0.4.0.0 (changelog) and ghc-stack-annotations 0.2.0.0 (changelog).

Wen rewrote ipedb’s LSM-tree database API (#18) and released 0.2.0.0/0.2.0.1 with other improvements (see the changelog).