The Butterfly Effect in Cabal

This is an elaboration on a point from my previous article, and an ensuing reddit comment.

Twitter has a unique language all its own, and it would be nice to be able to understand and do something cool with all those twitter tags… you know, @cdsmithus, #haskell, etc. The first step is to parse them from a block of text. So I hypothetically might decide to write a very simple Haskell package called “twittertags” to do that. Since I’d like my work to be extensible by others, I’ll export my work as parser combinators using parsec. Then other people can use my code for the oh-so-difficult task of recognizing special punctuation as tags on Twitter.

Now, I’m well aware that while the current version of Parsec is 3.1, quite a few people are still using 2.1 because of performance concerns (or just lack of time to update their code). Fortunately, if I import the compatible module names, I can be careful and make my code work with any version of parsec you like.

Of course, now that I’ve done the hard work of recognizing punctuation, people come along to use my library, building various Twitter and social media applications on top of this package of mine. In particular, let’s consider two such packages called “twitclient” and “superblog”. The both expose their own libraries, too, for extensibility. They want to use my parser combinators in their own, so they also depend on Parsec, but neither of these folks is going to test against two different Parsec versions! The author of “twitclient” decides to go with parsec 2.1, because of a measurable performance hit with the newer version. But the author of “superblog” is all for the latest versions, and depends on 3.1. My package works with both, so there’s no problem… right?

Wrong.

The dependencies now look like this…

Figure: The Cabal Butterfly Effect

Cabal allows several different versions of the same package to be installed at once, so there’s no problem with having both parsec versions installed at once. But the question is how my own library should be built. Should it be built against parsec 2.1? Or against parsec 3.1? The answer is both. We need versions of twittertags to built against both of the two dependencies, or else one of the two packages that depends on it will fail. But Cabal currently can’t do this.

What is does is certainly… suboptimal. When you build twitclient, it will recompile twittertags against parsec-2.1, which will break superblog. If you then reinstall superblog to fix it, Cabal will recompile twittertags against parsec-3.1, and break twitclient… and so on, ad infinitum. What’s worse, it will break these packages without even telling you that it is doing so!

That’s what I’m now dubbing the “Cabal Butterfly Effect”, and one of the issues I brought up in the earlier post.