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
WhileHandlingdropping theExceptionContextof the exception it is handling (!16275, #27456). - Sam made sure
SomeExceptiondoes not drop theExceptionContextof the exception it is storing (!16274, #27455), with accompanying CLC proposal #432. - Wolfgang added
rethrowSTM(!16501), incorporated the re-export into thestmsubmodule (!16616), and upstreamed it tostm(#100), with accompanying CLC proposal #436. - Zubin made
annotateSTMusecatchSTM#rather thancatch#(!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
- Andreas posted an informal GHC release status update on Discourse in June. GHC release progress is officially tracked on the GHC release status page.
- Zubin worked on 9.14.2, cutting a first release candidate at the end of July, and a second release candidate in September. This work fixed a significant number of bugs, many of them blockers for the next major release, 10.0, which Zubin will prepare in due course.
- Magnus prepared release candidates for 9.12.5.
- Andreas continued work backporting patches to the 9.10.4 branch.
Frontend
Magnus renamed
ZonkAnytoUnusedTypeand 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.Datainstance forHsCtxt(!16142, #27359).Sam reviewed a patch adding explanations for unsolved
Typeableconstraints (!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
mkTickin 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
implicitRequirementsShallowcan never reach aHoleUnit(!16346).Rodrigo deduplicated and shared
Modules in interface files (!16216, !16213, #27401).Hannes shared
UnitInfos across theUnitEnvusing aUnitIndexand an external unit database cache (!16115), and arranged for theUnitIndexCacheto be reused after initialising multiple home units (!16598, #27748).Hannes added the
backendInfoTableMapValiditybackend 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.alterpreserve insertion order (!16385).Wolfgang finished up a patch sorting
Usages by fingerprint, to ensure determinism (!15484).
Code generation
GHCi and bytecode
- Wolfgang added
--show-byte-code, the counterpart of--show-ifacefor 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.12upfront (!16132, #27317), rather than failing obscurely later on.David fixed the
ghc-internal.deffile 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-toolchainthrowing 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
process009test useSIGKILL(!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-changelogjob check for theno-changeloglabel in the script rather than inrules(!16177), which avoids having to re-run the entire pipeline when applying theno-changeloglabel.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_addchdirto be missing when linking againstprocesson Darwin (#377, #376).
Cabal
Releases
- The 3.18.1.0 release was managed by Artem Pelenitsyn, with release engineering help from Mikolaj (#12104, #12128, #12016, #12010, #12017, #12108, #12113, #12182).
Recompilation checking
Sam stopped
Cabalfrom writingsetup-configtwice (#11949), fixing access-denied failures (#11619).Sam made
cabal-installfall back to the external setup method on a Cabal-library mismatch (#11950), fixing stuck rebuilds ofbuild-type: Custompackages (#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 thecomponentsToLoadfield (#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
FileCachehook before typechecking (#5030), fixing #4675.Hannes ensured the
DocMapis 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
Magnus, Hannes and Andreas mentored the 2026 GSoC project by Enrico Maria De Angelis, adding back the case-split plugin to HLS (#5014).
Zubin and Hannes mentored the 2026 GSoC project by Vidit Odedra, adding go-to-definition for third-party dependencies to HLS (#4973, #5025).
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).
hdbhas 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
singleThreadflag of a continue request optional, as the Debug Adapter Protocol specifies (#38).Hannes made the stack frame decoding logic use the source locations that
StackAnnotationcarries fromghc-internal-9.1402onwards (#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).