The Haskell Programming Language's blog
blog.haskell.org.web.brid.gy
The Haskell Programming Language's blog
@blog.haskell.org.web.brid.gy
Analyzing language extension semantics
Hi I'm Jappie and I volunteer for the Haskell Foundation Stability Working Group. Recently we analyzed the head.hackage patches to understand why code breaks on new GHC releases. "head.hackage" is a repository of patches for Hackage. GHC engineers use these to test out new GHC builds on a wide range of Hackage packages without having to upstream1 a patch, which can take time. Instead, they can put the patch in "head.hackage" and immediately test it on a wide range of packages. Surprisingly, most breakage wasn’t caused by Template Haskell, it came from deeper semantic changes in language extensions. The meaning of (some) language extensions changed between GHC releases. This post walks through the main categories of breakage, why they happened, and what they tell us about long-term stability. If you care about a smoother upgrade path for Haskell users, we invite you to participate in the Haskell Foundation Stability Working Group. Extending our initial investigation, We're also interested in understanding _why_ breakage occurs. So we've recently done further analysis on head.hackage, and learned surprisingly enough that the root cause of a lot of breakage isn't Template Haskell, but seems to be from language extension semantics2. We're doing this investigation to understand better where efforts should be focused in improving stability. This gave us the following table: Name| Cause| Had warnings? ---|---|--- Cabal-2.4.1.0.patch| simplified subsumption| no Cabal-3.0.2.0.patch| simplified subsumption| no Cabal-3.2.1.0.patch| simplified subsumption| no data-r-tree-0.6.0.patch| parser change (see 1)| no drinkery-0.4.patch| simplified subsumption| no ghc-lib-parser-9.8.1.20231121.patch| rename forall identifiers (2)| yes hgeometry-ipe-0.13.patch| Instances moved due to splice enforcement| no singletons-3.0.2.patch| add TypeAbstractions as a language extension| yes singletons-base-3.1.1.patch| add TypeAbstractions as a language extension| yes vector-space-0.16.patch| Star is type (4)| yes `th-compat-0.1.4.patch` was miscounted so I left that out. Simplified subsumption appears a lot but 3 are for Cabal, so it's only 2 real occurrences. I'd expect that to appear a lot however, because it was one of _the_ motivating changes for a stability working group. ## Simplified subsumption For the blissfully ignorant reader simplified subsumption causes you to do this under certain existential conditions: --- a/Distribution/Simple/Utils.hs +++ b/Distribution/Simple/Utils.hs @@ -1338,7 +1338,7 @@ withTempFileEx opts tmpDir template action = (\(name, handle) -> do hClose handle unless (optKeepTempFiles opts) $ handleDoesNotExist () . removeFile $ name) - (withLexicalCallStack (uncurry action)) + (withLexicalCallStack (\x -> uncurry action x)) You have to insert a lambda, which apparently has some performance impact. This had a big impact on Yesod stacks, whose code generation helpfully created the database alias in the template: type DB a = forall (m :: Type -> Type). (MonadUnliftIO m) => ReaderT SqlBackend m a Normally this is quite convenient, however with the simplified subsumption change, any code that interacts with the database now has to insert those lambdas. As you can imagine this would in many places for a commercial code base. Causing a lot of compile errors for industrial users. Instead of inserting lambdas, you can also delete those existential aliases to solve the problem. Or you can enable the language extension: DeepSubsumption. Which restores the original behavior. ## Moving of instances due to Template Haskell This change forces you to put the instances above the splice where they are being used in the same module. A dear colleague decided to generate instances in Template Haskell. That was quite the puzzle! I asked the GHC devs why they did this, and it turns out this was a soundness issue in the typechecker. Here, soundness means the type system can't be tricked into allowing invalid programs. So the community is better off, despite this causing a fair bit of work. --- a/src/Ipe/Content.hs +++ b/src/Ipe/Content.hs @@ -288,6 +288,14 @@ +instance Fractional r => IsTransformable (IpeObject r) where + transformBy t (IpeGroup i) = IpeGroup $ i&core %~ transformBy t + ... makePrisms ''IpeObject @@ -303,14 +311,6 @@ -instance Fractional r => IsTransformable (IpeObject r) where - transformBy t (IpeGroup i) = IpeGroup $ i&core %~ transformBy t - ... ## (1) Parser change The parser is the component of the compiler that transforms text into a memory structure the compiler can work with. This structure is called an abstract syntax tree. - Node4 {getMBB :: {-# UNPACK #-} ! MBB, getC1 :: ! (RTree a), getC2 :: ! (RTree a), getC3 :: ! (RTree a), getC4 :: ! (RTree a) } - | Node3 {getMBB :: {-# UNPACK #-} ! MBB, getC1 :: ! (RTree a), getC2 :: ! (RTree a), getC3 :: ! (RTree a) } - | Node2 {getMBB :: {-# UNPACK #-} ! MBB, getC1 :: ! (RTree a), getC2 :: ! (RTree a) } + Node4 {getMBB :: {-# UNPACK #-} !MBB, getC1 :: !(RTree a), getC2 :: !(RTree a), getC3 :: !(RTree a), getC4 :: !(RTree a) } + | Node3 {getMBB :: {-# UNPACK #-} !MBB, getC1 :: !(RTree a), getC2 :: !(RTree a), getC3 :: !(RTree a) } + | Node2 {getMBB :: {-# UNPACK #-} !MBB, getC1 :: !(RTree a), getC2 :: !(RTree a) } | Node {getMBB :: MBB, getChildren' :: [RTree a] } - | Leaf {getMBB :: {-# UNPACK #-} ! MBB, getElem :: a} + | Leaf {getMBB :: {-# UNPACK #-} !MBB, getElem :: a} | Empty This is a change from all the way back in 2020, where the _core_ language changed by disallowing `!` before parens. Here the bang `!` indicates strict fields. Technically this doesn't fit into the category because the core language isn't a language extension. But semantics did change! Actually I don't think we expected to find something like this at all. I'm not sure how relevant this is to discuss further because it appears quite rare for someone to do this. You can enable StrictData in your Cabal file and delete all those bangs! ## (2) Rename forall identifiers This changes the forall identifier into a keyword at term level. It already was at the type level. The issue is discussed here hintExplicitForall :: Located Token -> P () hintExplicitForall tok = do - forall <- getBit ExplicitForallBit + forAll <- getBit ExplicitForallBit rulePrag <- getBit InRulePragBit - unless (forall || rulePrag) $ addError $ mkPlainErrorMsgEnvelope (getLoc tok) $ + unless (forAll || rulePrag) $ addError $ mkPlainErrorMsgEnvelope (getLoc tok) $ (PsErrExplicitForall (isUnicode tok)) ## (3) TypeAbstractions From what I understand from the manual is that part of the syntax for type abstractions landed in GHC 9.2, however 9.8 and onwards requires you to enable this language extension. This appeared because certain new functionality was introduced behind an old language extension flag, according to this proposal. It says we don't want to introduce new functionality behind established extensions, so that's why we require TypeAbstractions now, where previously ScopedTypeVariables and TypeApplications were enough. This extension enables you to bind type variables in pattern matches. I don't know why this happened like this, but it happened in 2023: +-- Invisible type binders in type declarations, such as +-- +-- type family Sing @k +-- +-- require the TypeAbstractions extension. +#if __GLASGOW_HASKELL__ >= 909 +{-# LANGUAGE TypeAbstractions #-} +#endif + ## (4) Star is type This change was announced via a warning. It tells users to write `Type` instead of `*` for kinds representing types. A kind is essentially the type of a type, and as a concept is used for type-level programming type safety. - type Basis v :: * + type Basis v :: Type ## Conclusion Often these breakages are annoying and frustrating. But if we look deeper, we find that each of them has a little story and good reasons for being introduced. If you find this all as interesting as I do, please consider joining some of the stability working group meetings! 1 Upstreaming is the process of sending a patch to the “maintainers” of an open-source project. The maintainers will then make the patch ‘official’ by merging it. In principle, the process is simple, but in practice, the burden of proof (especially for larger projects) is on the person who submitted the patch. They have to convince the maintainers that the patch is useful, which takes time in the form of communication 2 The precise meaning of features enabled by language extensions. I guess parser changes also count.
blog.haskell.org
December 1, 2025 at 9:51 AM
GHC 9.14.1-alpha1 is now available
The GHC developers are very pleased to announce the availability of the first alpha prerelease of GHC 9.14.1. Binary distributions, source distributions, and documentation are available at downloads.haskell.org. GHC 9.14 will bring a number of new features and improvements, including: * Significant improvements in specialisation: * The `SPECIALISE` pragma now allows use of type application syntax * The `SPECIALISE` pragma can be used to specialise for expression arguments as well as type arguments. * Specialisation is now considerably more reliable in the presence of `newtype`s * the specialiser is now able to produce specialisations with polymorphic typeclass constraints, considerably broadening its scope. * Significant improvements in the GHCi debugger * Record fields can be defined to be non-linear when `LinearTypes` is enabled. * `RequiredTypeArgments` can now be used in more contexts * SSE/AVX support in the x86 native code generator backend * A major update of the Windows toolchain * ... and many more A full accounting of changes can be found in the release notes. Given the many specialisation improvements and their potential for regression, we would very much appreciate testing and performance characterisation on downstream workloads. Due to unexpected complications, this initial prerelease comes a bit later than expected. Consequently, we expect to have three condensed alphas prior to the release candidate. We expect the next alpha will come the week of 9 Sept. 2025, while the third will come 23 Sept. 2025, with the release candidate coming 7 Oct. 2025. We would like to thank the Zw3rk stake pool, Well-Typed, Mercury, Channable, Tweag I/O, Serokell, SimSpace, the Haskell Foundation, and other anonymous contributors whose on-going financial and in-kind support has facilitated GHC maintenance and release management over the years. Finally, this release would not have been possible without the hundreds of open-source contributors whose work comprise this release. As always, do give this release a try and open a ticket if you see anything amiss.
blog.haskell.org
August 20, 2025 at 8:55 AM
Cabal 3.16 release
The Cabal release team brings you a new release of the cabal-install tool and accompanying libraries, version 3.16.0.0. This release supports the (not yet available) GHC 9.14, including its upcoming alpha. We expect to publish cabal-install 3.16.1.0 soon after GHC 9.14 is out and address any discovered issues and incompatibilities that can be fixed in the respective timeframe. The binaries for cabal-install are available: * In the GHCup main channel (we thank the GHCup maintainers for the swift support of our release). * On our website (the same set of binaries can also be installed via the GHCup vanilla channel); these binaries are signed by "Francesco Ariis francesco@ariis.it" (fingerprint: `DAFB 4D8A F684 1435 18D5 051F A9AF 0AAA 6B87 EC51`; the key is hosted on keyserver.ubuntu.com). * As usual, `cabal update && cabal install cabal-install-3.16.0.0` is an option too. ### What's new Some of the cool features in the release: * The new `cabal target` command; * Faster Git clones; * `cabal haddock-project` handles user-provided CSS; * Multiple `flags` stanzas in `cabal.project` accumulate values instead of using the last one; * `cabal gen-bounds` works with multi-package projects (i.e., embraces the `v2`-commands infrastructure); * Cabal multi-repl supports reexported-modules with renaming for GHC >= 9.12, and more! See the cabal-install release notes on Github for the full changelog. Power users may be interested in Cabal-the-library notes too. ### 3.16.0.0 Contributors The credits go to: Andreas Abel, Andreas Klebinger, Artem Pelenitsyn, Benjamin, Benjamin McRae, Berk Özkütük, Bodigrim, brandon s allbery kf8nh, Bryan Richter, Francesco Ariis, Francesco Gazzetta, Gleb Popov, Hécate Moonlight, James Blackburn, Jaro, Jasper Van der Jeugt, Javier Sagredo, Jens Petersen, Kazu Yamamoto, Kevin Quick, Leonid Znamenok, malteneuss, Matthew Pickering, Matt Parsons, Mike Pilgrem, Mikolaj Konarski, Moritz Angermann, noiioiu, parsonsmatt, Peter Becich, Phil de Joux, PHO, Praneya Kumar, Rebecca Turner, Rodrigo Mesquita, Sdywolf, Serge S. Gulin, Sergey Vinokurov, sheaf, Sylvain Henry, Teo Camarasu, theGhostJW, Tom Smeding, Trevis, Troels Henriksen, Yi Fang, Yuto Takano, zlonast, Zubin Duggal. We thank all the contributors as well as our reviewers, QA testers, devops, and others without whom this release wouldn’t be possible. ### Feedback Please report any issues you notice with the 3.16.0.0 release on our GitHub: https://github.com/haskell/cabal/issues — Cabal release team (Artem, Brandon, Francesco, Mikołaj)
blog.haskell.org
July 25, 2025 at 8:48 AM
GHC LTS Releases
# GHC will start maintaining an LTS release/branch in the near future A release being designated LTS (Long Term Support) in this case means we plan to support it over a longer timeframe than usual. Concretely the plan is to provide updates for a LTS releases for _at least_ two years. Most likely we will support LTS releases for even longer than that, aiming for a support window of three years currently. During this time we will be providing minor releases fixing bugs as with any other release. The main difference being that we will do so for a longer period of time. There are no plans to backport any new features to LTS releases after their initial release. In terms of frequency of LTS releases we plan to have an overlap between LTS support windows of different LTS series of six months. A potential timeline might then look like this: 2025 Aug - LTS 9.14 released 2028 Spring - LTS 9.22 released 2028 Summer - LTS 9.14.X - last 9.14 point release 2031 Spring - LTS 9.X released 2031 Summer - Last 9.22 point release … # Non-LTS releases GHC will continue to release new major non-lts releases on a ~6 Month cadence. We expect to cut back on the lifetime of these releases slightly, dedicating the resources freed up this way to enable a longer support window for the LTS releases. # Why LTS releases? In practice some releases always saw more adoption than others by users. The GHC Team has not been blind to this fact and has at times informally extended the life of a certain release based on this as well. This resulted in a sort of informal "post-hoc LTS" status of releases. At times with support windows not much shorter than our proposed minimum of two years. This worked reasonable well for people who were confident to stay on a fairly old release, only upgrading to a newer "post-hoc LTS" once the dust settled. It also worked out for those who picked one of those "post-hoc LTS" releases by happenstance before it was clear the release would end up as "post-hoc LTS". However users who adopted major releases which did not end up as "post-hoc LTS" often had to choose between upgrading earlier than expected, or risk running into a show stopping bug after the support window of the release had already ended. Similarly much of this was based on informal community sentiment and rarely written down explicitly. Making this information hard to access for members not deeply involved in the day to day of the haskell community. By designating a major release as LTS ahead of time we hope that users can make a informed decision about which GHC version they pick. Making it clear what the tradeoffs will be. With a clear choice between a longer support window or the newest features. # Why not make post-hoc LTS releases official instead? This is a question that has come up a lot in discussion. The major downsides of this are a lack of predictability, and that a lot of time might be lost between the initial release and any such decision. If we declare a release as LTS 9 months after its .1 release we essentially shaved off months from the LTS support window. On the flip side if we announce it ahead of time everyone knows that a given release will be the new LTS. So the hope is that this encourages more and quicker support for the release by the community. Hopefully compressing the timeline of bug fixing, testing and eventual widespread adoption. Overall I'm hopeful that LTS releases being explicit will remove a lot of ambiguity around GHC versions. And while the guaranteed LTS support window might not be as long as one might hope having LTS releases with longer guaranteed support window should still be helpful to people working on long running haskell projects. # Next steps The first LTS release will be GHC 9.14, which will be released this summer!
blog.haskell.org
July 16, 2025 at 8:46 AM