suggestion for a small extension to configurations

With the current issue of the split base library we have a lot of code like: flag split-base library if flag(split-base) build-depends: base >= 3.0, pretty, directory, etc else build-depends: base < 3.0 This does a backtracking search, checking both alternatives to see if one is satisfiable. It's a bit unsatisfactory however since we have to introduce a flag for a choice which really should be automatic and not visible to the user. It was always a deliberate part of the design of configurations that we should not let package authors make automatic decisions without the environment (packager or user) being able to influence the choice. So the question is, is there a special case that we can say really is an automatic decision that the user does not need to control with a flag? Here's my suggestion: library build-depends: base if package(base >= 3) build-depends: pretty, directory, etc and it is syntactic sugar for: flag _unnamed1 library build-depends: base if flag(_unnamed1) build-depends: base >= 3.0 build-depends: pretty, directory, etc else build-depends: !(base >= 3.0) So we introduce a new predicate 'package()' that tests the version number of a package that we've already declared that we depend on. It is crucial that it be one we already depend upon. It does not test packages in the environment generally, it tests the version of the package that the environment has or might impose. So we still introduce a backtracking point, it's just that now it's an backtracking without a named flag. This is ok because the decision about which branch to take is completely determined once the environment picks a version for the dependent package in question. Details... each package predicate test introduces it's own distinct anonymous flag. The flags are introduced in order, top to bottom, left to right and appear after all other named flags. eg: flag blah build-depends: foo, bar if package(foo > 1) && package (bar > 1) && flag(blah) ... else ... => flag blah flag _1 flag _2 build-depends: foo, bar if flag(_1) && flag(_2) && flag(blah) build-depends: foo > 1 build-depends: bar > 1 ... else build-depends: !(foo > 1) build-depends: !(bar > 1) ... Saying that we already depend on a package is interpreted relative to the position of the package() test in the overall condition tree. It means that to get to the branch where the package() test is, we must have that package in the build-depends explicitly in some parent node. Possibly as an extension we could allow it to appear in all the alternatives of some sibling node eg: So, overall, does that make sense? Is that sufficiently precise? Should we do it? Here's the canonical example. It's a package (tar) that works with ghc-6.4, 6.6 and 6.8 and uses bytestring: Flag bytestring-in-base Flag split-base library if flag(bytestring-in-base) -- bytestring was in base-2.0 and 2.1.1 build-depends: base >= 2.0 && < 2.2 else -- in base 1.0 and 3.0 bytestring is a separate package build-depends: base < 2.0 || >= 3, bytestring >= 0.9 if flag(split-base) build-depends: base >= 3.0, directory, old-time else build-depends: base < 3.0 which we could simplify to: library build-depends: base if package(base >= 3) directory, old-time if package(base < 2 || >= 3) build-depends: bytestring >= 0.9 Much nicer I think. Duncan

On Tue, Oct 16, 2007 at 11:15:14PM +0100, Duncan Coutts wrote:
each package predicate test introduces it's own distinct anonymous flag. The flags are introduced in order, top to bottom, left to right and appear after all other named flags.
I don't have strong opinions either way (I'd say the extra complexity wasn't worthwhile personally, but you've had more experience with writing cabal configurations), but one thing that occurs to me: are people likely to write things like this?: if (base >= 3.0) build-depends: pretty if (base >= 3.0) build-depends: containers [...] If so then we'll want to spot that and only invent a single flag, or the exponential number of combinations might become significant. Thanks Ian

On Tue, Oct 16, 2007 at 11:15:14PM +0100, Duncan Coutts wrote:
Here's my suggestion:
library build-depends: base if package(base >= 3) build-depends: pretty, directory, etc
and it is syntactic sugar for:
flag _unnamed1
library build-depends: base if flag(_unnamed1) build-depends: base >= 3.0 build-depends: pretty, directory, etc else build-depends: !(base >= 3.0)
Your examples use base, for which there will be only one version in a GHC installation. But there might be multiple versions of other packages, so testing on them is a bit more complicated. How does a repackager control the selection? (On the other hand, it may be that flags are to coarse a control for them anyway; they might want to specify versions or version ranges too.)
So we still introduce a backtracking point, it's just that now it's backtracking without a named flag. This is ok because the decision about which branch to take is completely determined once the environment picks a version for the dependent package in question.
If this is just sugar for flags, won't it still backtrack just as much? Also, when one branches on a flag, the first alternative is preferred if both work. Presumably this will still be the case, so one might sometimes have to write an inverted test with an empty then-branch.

On Mon, 2007-11-05 at 12:17 +0000, Ross Paterson wrote:
Your examples use base, for which there will be only one version in a GHC installation. But there might be multiple versions of other packages, so testing on them is a bit more complicated.
How does a repackager control the selection?
I was hoping they would not have to. In fact that was rather the point, that this package predicate should identify a special case where a user visible flag is not necessary.
(On the other hand, it may be that flags are to coarse a control for them anyway; they might want to specify versions or version ranges too.)
There is certainly a case to be made for a user/packager wanting to throw in additional constraints, like bytestring <= 0.9 when they have bytestring-1.0 available. Indeed just for testing purposes it's useful to be able to control which version of a package is chosen when there are several possible (satisfiable) choices.
So we still introduce a backtracking point, it's just that now it's backtracking without a named flag. This is ok because the decision about which branch to take is completely determined once the environment picks a version for the dependent package in question.
If this is just sugar for flags, won't it still backtrack just as much?
Yes. I was confused. Can anyone see a rationale in putting these extra generated flags last? It does change the order in which we explore the space of flag assignments. Given the restricted form of constraints that package predicates give rise to, should we put the extra flags first or last? (first/last wrt to any other explicitly declared flags)
Also, when one branches on a flag, the first alternative is preferred if both work. Presumably this will still be the case, so one might sometimes have to write an inverted test with an empty then-branch.
Yes, or if you need something special you can use the existing flag syntax I guess. Duncan
participants (3)
-
Duncan Coutts
-
Ian Lynagh
-
Ross Paterson