Making GHC upgrades easy

When a shiny new version of GHC comes out, it should be easy to upgrade. After all, the new compiler should be more capable than the previous version!

@simonpj explains in details what has been done, what is now possible, and what remains to be done in this quest of Making GHC upgrades easy.

35 Likes

A great summary, very helpful!

2 Likes

Exciting times! A lot of people (myself regrettably included) have historically used base bounds as a proxy for “which GHC release is this package compatible with?”. What’s the best way to indicate this moving forward? I suppose once base is genuinely reinstallable it’ll be less of a problem unless you deliberately depend on GHC internals, and sensible PVP bounds for base will get you a working build plan with new compilers?

1 Like

There is still a need to be able to define what ghc version you need, since language extensions and the like aren’t part of base obviously.

This is indeed a bit of a lingering question, with some more discussion here: Find a new way of specifying compiler compatiblity · Issue #5 · well-typed/reinstallable-base · GitHub

It is possible for a .cabal file to use conditionals that depend on GHC version, but that isn’t a perfect solution: it can’t be changed retrospectively via revisions, and it may accidentally cause problems for other compilers (e.g. MicroHS) if the constraints assume the use of GHC.

Ideally, we would minimize changes to the semantics of existing language extensions, and would instead add new extensions. That way packages can use other-extensions in their .cabal file to indicate what they need in a declarative way, and (in principle) any compiler supporting those extensions should work. However, not all extensions are well-specified and there are sometimes bugs or compiler behaviour changes meaning packages need to express more fine-grained constraints. Perhaps we should have a notion of versioning for language extensions…

5 Likes

Great work! Looking forward to base jumping with the rest of you:)

2 Likes

I hope you were joking, but just in case: the compiler version range + the extension exactly specifies the semantics expected of the extension. Of course if the upper boundary is not specified, that would express hope that any future modifications to the language extension would be backward-compatible.

1 Like

The upper boundary of what exactly? As far as I understood it, one can currently not specify in the cabal file with which compilers/which version of the compiler the package is compatible. People tend(ed) to use the upperbound on ‘base’ for this; but with the plan of decoupling base from ghc that will not work anymore.

1 Like

The Cabal reference already gives an example:

if impl(ghc >= 7.5)
  other-extensions: PolyKinds

Now suppose GHC 10.2 comes up with a new and improved PolyKinds which alas has a corner case that breaks our package. Then we might want to say

if impl(ghc >= 7.5) && impl(ghc < 10.2)
  other-extensions: PolyKinds

Unfortunately while this is informative it also does next to nothing, we still need to come up with a workaround for PolyKinds in every affected module and maybe guard it with CPP. But my only point was that it’s semantically correct to equate the “extension version” with extension name + compiler version range, and the latter would be preferable because it doesn’t introduce any new concept.

But it does require introducing the concept of version ranges on GHC.

Anyway, I think the solution is just to avoid breaking changes in stable language extensions.

1 Like

The issue being that this just enables or disables the extensions enabled instead of giving you version bounds.

You could simulate this by giving impossible build dependencies for certain ghc versions:

if impl(ghc <= 7.5)
  build-depends: base < 0

But this wouldn’t be a satisfactory solution since you then can’t use the cabal constraint solver here.

You could add a dependency to the ghc or ghc-internal or ghc-experimental packages, but for most packages these are pointless dependencies, and this obviously wouldn’t work for alternative haskell compilers like mhs.

Yes, allowing extensions to be versioned would be an absolute mess for end users to deal with and incentivize breaking changes.

PVP was a failure. It made people not consider to carefully design their API and instead be in a constant state of experimentation.

Let’s not open these flood gates for language extensions.

1 Like

The point of being able to version extensions would be to distinguish them from the current version of the compiler. In principle it would be nice to be able to say “this program requires FancyExtension-1.0” and any GHC (or other compiler) version that supported FancyExtension-1.0 would continue to work, even if it had also introduced FancyExtension-2.0 with breaking changes in the meantime.

That said, as @hasufell says this risks introducing a lot of complexity, both for users and for compiler implementers. Certainly keeping extensions stable is preferable where possible. But there’s a genuine tension between maintaining stability and wanting to fix deficiencies in existing designs.

3 Likes