Summer of Code idea: cabal resolver errors

Haskell Summer of Code is currently soliciting ideas. One change that I’d like to see to cabal is a clearer formatting of resolver errors, to make it easier to understand what the fix should be. I’ve pasted a sample resolver error below. One, possibly naive, suggestion is that instead of giving 20 lines of exact versions, summarize them, e.g. rather than

rejecting: pretty-simple:setup.base-4.17.2.1,
pretty-simple:setup.base-4.17.2.0, pretty-simple:setup.base-4.17.1.0,
pretty-simple:setup.base-4.17.0.0, pretty-simple:setup.base-4.16.4.0,
...

instead say

rejecting: all pretty-simple:setup-base in the range 3.0.3.1-4.17.2.1

Unfortunately, I don’t currently know enough about cabal to say whether this and similar improvements are a Hard Problem^{TM} which can’t be reasonably resolved from the standpoint of constraint solver theory, or whether they’re hard to implement for some reason, or whether they’re simply not feasible for some other reason. Nor would I be able to supervise such a project without doing a lot of reading up on the current implementation.

I’d like to check whether other people would consider this a worthwhile improvement, and whether anyone knows if there’s any existing work in that area, and/or how it would be implemented in practice. If there’s someone who feels confident and excited about supervising such a project then great, please propose it! I will be in support. On the other hand if there is enough enthusiasm about the project but no one able to superivise it then maybe I can learn enough about cabal myself to supervise it.


cabal: Could not resolve dependencies:
[__0] trying: stan-0.1.2.0 (user goal)
[__1] trying: pretty-simple-4.1.2.0 (dependency of stan)
[__2] next goal: pretty-simple:setup.Cabal (dependency of pretty-simple)
[__2] rejecting: pretty-simple:setup.Cabal-3.10.2.0/installed-544a,
pretty-simple:setup.Cabal-3.10.2.1, pretty-simple:setup.Cabal-3.10.2.0,
pretty-simple:setup.Cabal-3.10.1.0, pretty-simple:setup.Cabal-3.8.1.0
(constraint from maximum version of Cabal used by Setup.hs requires <3.8)
[__2] trying: pretty-simple:setup.Cabal-3.6.3.0
[__3] next goal: pretty-simple:setup.unix (dependency of
pretty-simple:setup.Cabal)
[__3] rejecting: pretty-simple:setup.unix-2.8.3.0/installed-596c (conflict:
pretty-simple:setup.Cabal => pretty-simple:setup.unix>=2.6.0.0 && <2.8)
[__3] skipping: pretty-simple:setup.unix-2.8.5.0,
pretty-simple:setup.unix-2.8.4.0, pretty-simple:setup.unix-2.8.3.0,
pretty-simple:setup.unix-2.8.2.1, pretty-simple:setup.unix-2.8.2.0,
pretty-simple:setup.unix-2.8.1.1, pretty-simple:setup.unix-2.8.1.0,
pretty-simple:setup.unix-2.8.0.0 (has the same characteristics that caused the
previous version to fail: excluded by constraint '>=2.6.0.0 && <2.8' from
'pretty-simple:setup.Cabal')
[__3] trying: pretty-simple:setup.unix-2.7.3
[__4] next goal: pretty-simple:setup.base (dependency of pretty-simple)
[__4] rejecting: pretty-simple:setup.base-4.19.0.0/installed-e327 (conflict:
pretty-simple:setup.unix => pretty-simple:setup.base>=4.10 && <4.18)
[__4] skipping: pretty-simple:setup.base-4.19.0.0,
pretty-simple:setup.base-4.18.2.0, pretty-simple:setup.base-4.18.1.0,
pretty-simple:setup.base-4.18.0.0 (has the same characteristics that caused
the previous version to fail: excluded by constraint '>=4.10 && <4.18' from
'pretty-simple:setup.unix')
[__4] rejecting: pretty-simple:setup.base-4.17.2.1,
pretty-simple:setup.base-4.17.2.0, pretty-simple:setup.base-4.17.1.0,
pretty-simple:setup.base-4.17.0.0, pretty-simple:setup.base-4.16.4.0,
pretty-simple:setup.base-4.16.3.0, pretty-simple:setup.base-4.16.2.0,
pretty-simple:setup.base-4.16.1.0, pretty-simple:setup.base-4.16.0.0,
pretty-simple:setup.base-4.15.1.0, pretty-simple:setup.base-4.15.0.0,
pretty-simple:setup.base-4.14.3.0, pretty-simple:setup.base-4.14.2.0,
pretty-simple:setup.base-4.14.1.0, pretty-simple:setup.base-4.14.0.0,
pretty-simple:setup.base-4.13.0.0, pretty-simple:setup.base-4.12.0.0,
pretty-simple:setup.base-4.11.1.0, pretty-simple:setup.base-4.11.0.0,
pretty-simple:setup.base-4.10.1.0, pretty-simple:setup.base-4.10.0.0,
pretty-simple:setup.base-4.9.1.0, pretty-simple:setup.base-4.9.0.0,
pretty-simple:setup.base-4.8.2.0, pretty-simple:setup.base-4.8.1.0,
pretty-simple:setup.base-4.8.0.0, pretty-simple:setup.base-4.7.0.2,
pretty-simple:setup.base-4.7.0.1, pretty-simple:setup.base-4.7.0.0,
pretty-simple:setup.base-4.6.0.1, pretty-simple:setup.base-4.6.0.0,
pretty-simple:setup.base-4.5.1.0, pretty-simple:setup.base-4.5.0.0,
pretty-simple:setup.base-4.4.1.0, pretty-simple:setup.base-4.4.0.0,
pretty-simple:setup.base-4.3.1.0, pretty-simple:setup.base-4.3.0.0,
pretty-simple:setup.base-4.2.0.2, pretty-simple:setup.base-4.2.0.1,
pretty-simple:setup.base-4.2.0.0, pretty-simple:setup.base-4.1.0.0,
pretty-simple:setup.base-4.0.0.0, pretty-simple:setup.base-3.0.3.2,
pretty-simple:setup.base-3.0.3.1 (constraint from non-upgradeable package
requires installed instance)
[__4] fail (backjumping, conflict set: pretty-simple,
pretty-simple:setup.base, pretty-simple:setup.unix)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: pretty-simple:setup.Cabal,
pretty-simple:setup.unix, pretty-simple:setup.base, pretty-simple, stan
Try running with --minimize-conflict-set to improve the error message.
6 Likes

I think there are many more low hanging improvements in cabal’s error messages.

For example in the error that you show the first part is about Cabal and unix:

[__2] next goal: pretty-simple:setup.Cabal (dependency of pretty-simple)
[__2] rejecting: pretty-simple:setup.Cabal-3.10.2.0/installed-544a,
pretty-simple:setup.Cabal-3.10.2.1, pretty-simple:setup.Cabal-3.10.2.0,
pretty-simple:setup.Cabal-3.10.1.0, pretty-simple:setup.Cabal-3.8.1.0
(constraint from maximum version of Cabal used by Setup.hs requires <3.8)
[__2] trying: pretty-simple:setup.Cabal-3.6.3.0
[__3] next goal: pretty-simple:setup.unix (dependency of
pretty-simple:setup.Cabal)
[__3] rejecting: pretty-simple:setup.unix-2.8.3.0/installed-596c (conflict:
pretty-simple:setup.Cabal => pretty-simple:setup.unix>=2.6.0.0 && <2.8)
[__3] skipping: pretty-simple:setup.unix-2.8.5.0,
pretty-simple:setup.unix-2.8.4.0, pretty-simple:setup.unix-2.8.3.0,
pretty-simple:setup.unix-2.8.2.1, pretty-simple:setup.unix-2.8.2.0,
pretty-simple:setup.unix-2.8.1.1, pretty-simple:setup.unix-2.8.1.0,
pretty-simple:setup.unix-2.8.0.0 (has the same characteristics that caused the
previous version to fail: excluded by constraint '>=2.6.0.0 && <2.8' from
'pretty-simple:setup.Cabal')
[__3] trying: pretty-simple:setup.unix-2.7.3

But that has no influence at all on the base dependency which is what finally causes the problem.

Basically the only part you need to know the whole problem is this one line:

[__4] rejecting: pretty-simple:setup.base-4.19.0.0/installed-e327 (conflict:
pretty-simple:setup.unix => pretty-simple:setup.base>=4.10 && <4.18)

And maybe this hint which is moved all the way to the bottom for some reason:

(constraint from non-upgradeable package requires installed instance)

But yeah, starting with a smaller version range representation would already be a big improvement.

Edit: I see now the Cabal and unix parts are relevant. But essentially it could be summarized as:

  • pretty-simple’s Setup.hs requires Cabal <3.8
  • Cabal 3.6.3.0 which satisfies that requires unix <2.8
  • unix 2.7.3 which satisfies that requires base <4.18
  • You are using base 4.19 and base is not reinstallable
3 Likes

Unfortunately it’s worse than that, because Setup.hs doesn’t require Cabal <3.8, that’s just the misleading error message that is output when you use a version of cabal-install that’s <3.8. See Why is latest version of cabal sometimes required to build?

1 Like

Ah so it should be:

  • stan depends on pretty-simple, choosing 4.1.2.0
  • pretty-simple-4.1.2.0 uses a custom build-type so requires Cabal no newer than the version in use, choosing 3.6.3.0
  • Cabal-3.6.3.0 requires unix <2.8, choosing 2.7.3
  • unix-2.7.3 requires base <4.18
  • You are using base-4.19 and base is not reinstallable

I feel like it should be possible to make the error messages look like this, but perhaps that is too big a project for the summer of code.

2 Likes

Yeah, that’s right. The error message would also have to explain why versions that it didn’t pick don’t work either.

I don’t think there are any versions that it didn’t pick that my summary didn’t explain, are there?

I guess the only unexplained alternatives are pretty-simple <4.1.2.0, Cabal <3.6.3.0, and unix <2.7.3, but the original message doesn’t explain those either.

1 Like

Yeah, those are the versions it didn’t pick that need to be explained. I wonder why the original error messaged didn’t explain them either. Perhaps it’s an artefact of custom setup. I’m pretty sure it normally explains it! For example, look at this failure due to a text dependency. It explains all texts back to the very beginning, text-0.1:

% cabal-3.10.1.0 repl -z -b 'Cabal==3.8.1.0' -w ghc-9.8 
Resolving dependencies...
Error: cabal-3.10.1.0: Could not resolve dependencies:
[__0] trying: fake-package-0 (user goal)
[__1] trying: base-4.19.0.0/installed-e327 (dependency of fake-package)
[__2] next goal: Cabal (dependency of fake-package)
[__2] rejecting: Cabal-3.10.2.0/installed-544a (conflict: fake-package =>
Cabal==3.8.1.0)
[__2] skipping: Cabal-3.10.2.1, Cabal-3.10.2.0, Cabal-3.10.1.0 (has the same
characteristics that caused the previous version to fail: excluded by
constraint '==3.8.1.0' from 'fake-package')
[__2] trying: Cabal-3.8.1.0
[__3] next goal: text (dependency of Cabal)
[__3] rejecting: text-2.1/installed-0c74 (conflict: Cabal => text>=1.2.3.0 &&
<1.3 || >=2.0 && <2.1)
[__3] skipping: text-2.1 (has the same characteristics that caused the
previous version to fail: excluded by constraint '>=1.2.3.0 && <1.3 || >=2.0
&& <2.1' from 'Cabal')
[__3] rejecting: text-2.0.2 (conflict: base =>
ghc-prim==0.11.0/installed-5690, text => ghc-prim>=0.2 && <0.11)
[__3] rejecting: text-2.0.1 (conflict: base =>
ghc-prim==0.11.0/installed-5690, text => ghc-prim>=0.2 && <0.10)
[__3] rejecting: text-2.0, text-1.2.5.0 (conflict: base =>
ghc-prim==0.11.0/installed-5690, text => ghc-prim>=0.2 && <0.9)
[__3] rejecting: text-1.2.4.1 (conflict: base==4.19.0.0/installed-e327, text
=> base>=4.3 && <4.16)
[__3] rejecting: text-1.2.4.0 (conflict: base =>
ghc-prim==0.11.0/installed-5690, text => ghc-prim>=0.2 && <0.6)
[__3] rejecting: text-1.2.3.2 (conflict: base==4.19.0.0/installed-e327, text
=> base>=4.14 && <4.15)
[__3] skipping: text-1.2.3.1, text-1.2.3.0, text-1.2.2.2, text-1.2.2.1,
text-1.2.2.0, text-1.2.1.3, text-1.2.1.2, text-1.2.1.1, text-1.2.1.0,
text-1.2.0.6, text-1.2.0.5, text-1.2.0.4, text-1.2.0.3, text-1.2.0.2,
text-1.2.0.0 (has the same characteristics that caused the previous version to
fail: excludes 'base' version 4.19.0.0)
[__3] rejecting: text-1.1.1.4 (conflict: Cabal => text>=1.2.3.0 && <1.3 ||
>=2.0 && <2.1)
[__3] skipping: text-1.1.1.3, text-1.1.1.2, text-1.1.1.1, text-1.1.1.0,
text-1.1.0.1, text-1.1.0.0, text-1.0.0.1, text-1.0.0.0, text-0.11.3.1,
text-0.11.3.0, text-0.11.2.3, text-0.11.2.2, text-0.11.2.1, text-0.11.2.0,
text-0.11.1.13, text-0.11.1.12, text-0.11.1.11, text-0.11.1.10, text-0.11.1.9,
text-0.11.1.8, text-0.11.1.7, text-0.11.1.6, text-0.11.1.5, text-0.11.1.3,
text-0.11.1.2, text-0.11.1.1, text-0.11.1.0, text-0.11.0.8, text-0.11.0.7,
text-0.11.0.6, text-0.11.0.5, text-0.11.0.4, text-0.11.0.3, text-0.11.0.2,
text-0.11.0.1, text-0.11.0.0, text-0.10.0.2, text-0.10.0.1, text-0.10.0.0,
text-0.9.1.0, text-0.9.0.1, text-0.9.0.0, text-0.8.1.0, text-0.8.0.0,
text-0.7.2.1, text-0.7.1.0, text-0.7.0.1, text-0.7, text-0.6, text-0.5,
text-0.4, text-0.3, text-0.2, text-0.1 (has the same characteristics that
caused the previous version to fail: excluded by constraint '>=1.2.3.0 && <1.3
|| >=2.0 && <2.1' from 'Cabal')
[__3] fail (backjumping, conflict set: Cabal, base, text)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: Cabal, text, base, fake-package
Try running with --minimize-conflict-set to improve the error message.

I think it only does that for the innermost failing dependency, like pretty-simple:setup.base in your original example.

In my summary I just say “base is not reinstallable” for that case.

1 Like

Oh I see. I think I’d need to construct (or discover) a more complex case to see what other sorts of backtracking cabal does, and what it says it has done when it does so. Anyway, in your example, unix-2.7.2 might support base-4.19, in principle even if it’s unlikely in practice, so the fact that it has or has not considered 2.7.2 needs to be made available somehow.

I think it’s fine to leave that implicit like in the original message. So it should be assumed it has not tried that because it doesn’t mention that version and the constraints that are mentioned don’t rule out that option. Since this is so uncommon I don’t think it’s necessary to make this more explicit.

One thing I didn’t show is if there would be unix-2.7.5 and unix-2.7.4 package which don’t work because of some other incompatibility, then I’d suggest a message like:

  • Cabal-3.6.3.0 requires unix <2.8
  • Skipping unix-2.7.5 through unix-2.7.4 because pretty-simple requires unix <2.7.4, choosing 2.7.3
  • unix-2.7.3 requires base <4.18
1 Like

Oh, if it really hasn’t tried it then I agree it’s fine not to mention so. But I assumed it did try it! Why wouldn’t it? Doesn’t the solver actually check exhaustively?

We’re working on it, first with a fix for cabal#9559 that shortens skipping and rejection messages by not repeating the package name hyphenated with each version.

- [__0] rejecting: pandoc-3.1.8, pandoc-3.1.7, pandoc-3.1.6.2, pandoc-3.1.6.1, pandoc-3.1.6, pandoc-3.1.5, pandoc-3.1.4
+ [__0] rejecting: pandoc; 3.1.8, 3.1.7, 3.1.6.2, 3.1.6.1, 3.1.6, 3.1.5, 3.1.4
3 Likes

I don’t know cabal well enough to tell for sure, but I assumed the error message was basically a log of what it does, so if it doesn’t mention checking those alternatives then I can only assume it hasn’t tried them.

1 Like

Great, thanks! Do you think there’s additional Summer Of Codeable work that could be partitioned up and offered as potential projects?

See also Improving Cabal Solver Output for Better Readability and Usability · Issue #8939 · haskell/cabal · GitHub

2 Likes

Ah, that also solves my misunderstanding:

grayjay writes:
Unfortunately, cabal only prints the first conflicts by default. These conflicts show why cabal couldn’t use the most preferred versions, but not why it failed overall. -v3 shows all of the backtracking.

1 Like

Thanks for the replies all. I get the impression this is all in hand at Cabal HQ, so I don’t intend to take any further action on this.