
I believe that a way to avoid orphan instances without needing huge modules
(such as my instance declaration idea) would help a lot in this regard,
long term—the smaller modules are, the less trouble changes cause and the
better a system you propose can work.
On Feb 26, 2015 8:36 AM, "Joachim Breitner"
Hi,
= Introduction (can be skipped) =
Recently, I find myself often worried about stability of our ecosystem – may it be the FTP discussion, or the proposed¹ removal of functions you shouldn’t use, like fromJust. While all that makes our language and base libraries nicer, it also means that my chances that code from 3 or 4 years ago will compile without adjustment on newer compilers are quite low.
Then I remember that a perl based webpage that I wrote 12 years ago just keeps working, even as I upgrade perl with every Debian stable release, and I’m happy about that. It makes me wonder if I am ever going to regret using Haskell because there, I’d have to decide whether I want to invest time to upgrade my code or simply stop using it.
I definitely do not want to stop Haskell from evolving. Maybe we can make a more stable Haskell experience opt-in? This is one step in that direction, solving (as always) only parts of the problem.
= The problem =
One problem is that our central library "base" is both an implementation and an interface. As the implementation is tied to the compiler, and the interface comes with the implementation, you cannot upgrade one without the other.
= My solution: frozen-base =
My solution would be to provide a new library, let’s call it frozen-base, which decouples the interface (frozen-base) from the implementation (still base).
We would start with one particular version of base, say, 4.6. With GHC-7.6 (which comes with base 4.6), frozen-base would simply re-export its modules². On newer compilers and newer versions of base, it would re-create the old interface using CPP. So if your program depends on frozen-base only, you can expect it to compile with newer versions of GHC – achievement unlocked.
= How to get new features, then? =
But does that mean that you do not get to use great new functionality in later versions of base, such as Data.Bool.bool? No: When a new version of base gets released, and a module gets changed, than frozen-base will ship a _new_ module, named Data.Bool1 that matches that interface. The next change in base causes yet another module to be created, i.e. Data.Bool2 So if you need to use Data.Bool.bool, in one of your modules, you simply change the import from "import Data.Bool" to "import Data.Bool2", adjust your code to the new interface, and are ready to go. Note that you only had to adjust to the changes in Data.Bool, nothing else. Note that you also had to touch only a single module in your program, the others still use whatever interface they were using.
Your dependency on frozen-base would have to indicate a lower version bound, i.e. specify the lowest version that has all the Data.Bool<n> variants that you desire, but – by design – never an upper version.³
= When does it not work? =
Of course, the promises by froze-base are not absolute: There are changes in base that frozen-base cannot protect you against: Data type changes, some type class changes, type class instances. I don’t have any great ideas here, but maybe they are rare enough so that frozen-base is still useful.
= What about the Prelude? =
I did not talk about the Prelude. There probably is not a good solution, and I’d simply leave the Prelude frozen. Maybe later GHC has a nice way to specifying which Prelude to use in a certain module, then you could use "Prelude1", "Prelude2" etc. in the same manner.
= How does it related to base-compat? =
The idea is similar to what base-compat provides, but the other way around: base-compat allows you to use the latest features of base on older compilers, while frozen-base would allow you to use the API of old base versions on newer systems.
Frozen-base might subsume base-compat by providing, say Data.Bool2 with the API from base-4.8 also on systems with base-4.6.
= How does it related to the split base proposal =
Depending on how exactly base is being reconstructed, it might become a pure interface package on its own, or at least independent from compiler-specific bits. Then it might be feasible that, say, after the release of ghc-7.10 there is a new upload of base-4.6.0.n that provides the 4.6 API, but compiles on ghc-7.10. This way, if you depend on base == 4.6.* you could still expect your program to work. If that happens, and also cabal would learn that a plan with different versions of a interface only library like base are not a problem, frozen-base might be obsoleted.
See https://ghc.haskell.org/trac/ghc/wiki/SplitBase for more on that.
= No Conclusion and lots of future work =
So what do you think? Would you be interested in using this? Do you have reasons to believe that this will not work as nicely as I think i could?
Greetings, Joachim
¹ but rejected, it seems ² or a sensible subset; I could imagine not exporting certain .Internal modules and things like OldTypeable that are about compatibility themselves. ³ or just one on the very major version number (frozen-base < 2), which to allow for exceptional breaking changes – a redesign of the module naming scheme for example.
-- Joachim “nomeata” Breitner mail@joachim-breitner.de • http://www.joachim-breitner.de/ Jabber: nomeata@joachim-breitner.de • GPG-Key: 0xF0FBF51F Debian Developer: nomeata@debian.org
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe