Constraint language: add a positive way to automatically enable flags
Opened this issue · 6 comments
If I want to enable a flag based on some conditions, I have to do hackery like (N) [1]:
if os(windows)
if flag(_regex-posix-clib)
build-depends: base<0
I'd rather be able to state this positively, e.g. (P):
if os(windows)
flag: _regex-posix-clib
It seems like (N) isn't understood by stack [2].
In general, it seems that logic isn't first-class in the cabal language. Some propositions can only occur negatively, it seems (like flag, os, true, false). Positive occurrence seems to be reserved to build-depends propositions (and maybe some few more).
For a start, why cannot false occur positively?, to allow me to write:
if os(windows)
if flag(_regex-posix-clib)
false
(That's not my ultimate goal, of course.)
[1] haskell-hvr/regex-posix@13c28e9#r47401775
[2] commercialhaskell/stack#5404
Flags are toggled from the "outside". The only thing you can do "inside" is to check that configuration is valid.
For example, one can try to compile on Windows with -f+_regex-posix-clib.
stack specifically doesn't let you flip flags on will. They are all fixed in the snapshot resolver definition. That is design issue in how Stackage works, as far as I can tell.
I don't understand what you mean by "occurs positively / negatively".
To point to related ideas:
I proposed sometime in the past for these cases to augment the cabal flag definition syntax to change the default: field to accept a boolean expression, rather than only a constant primitive bool value: #5328
Different platforms tend to have different preferred default configurations; that feature would help with that aspect
The other aspect of being able to express a Prolog-ish fail/0 more first-level and expressive than build-depends: base<0 was the subject of a different ticket (discussion) I can't find right now; the idea was similar to the false statement suggested above:
if os(windows)
if !flag(_regex-posix-clib)
fail: "windows doesn't provide regex(3) in its libc; please enable _regex-posix-clib"
or variant of this inspired by Python's assert (but to be considered under propositional logic semantics):
assert: os(windows) => flag(_regex-posix-clib), "windows doesn't provide regex(3) in its libc; please enable _regex-posix-clib"
(the example above assumes a new logic operator to denote logical implication to keep the example more obvious)
@phadej wrote:
Flags are toggled from the "outside". The only thing you can do "inside" is to check that configuration is valid.
The spec says something else:
If the user does not fix the value of a flag, Cabal will try to find a flag assignment in the following way.
For each flag specified, it will assign its default value, evaluate all conditions with this flag assignment, and check if all dependencies can be satisfied. If this check succeeded, the package will be configured with those flag assignments.
If dependencies were missing, the last flag (as by the order in which the flags were introduced in the package description) is tried with its alternative value and so on. This continues until either an assignment is found where all dependencies can be satisfied, or all possible flag assignments have been tried.
So flags can be forced by constraints. Atm, only indirectly ("negatively"), by producing contraditions from specific flag assignments. But we could as well force them directly ("positively".
I don't understand what you mean by "occurs positively / negatively".
Sorry, this is logic speak.
In N implies P, N occurs negatively and P occurs positively. Negative occurrences are in negations and in conditions of implications. In the case of cabal, negative occurrences are in the conditions of an if, and positive occurrences are in the "actions" (e.g. build-depends: foo).
I guess I meant "inside as positively. Consider an example
flag someflag
library
flag: someflag
this is most likely nonsense. We force flag toggling because of some effect that flag will have.
So what Herbert says: having a way to express fail or assert directly would be better (and a lot easier to implement to, though still far from trivial, IIRC).
I don't think we need to allow flags (or os(windows) things) to occur "positively".
EDIT: One could translate
library
flag: someflag
into
library
if !flag(someflag)
fail: "inconsistency"
But this is not exactly the same. I'm worried that flag: someflag will be misunderstood.
I don't think we need to allow flags (or
os(windows)things) to occur "positively".
I suppose having os to occur positively would be confusing, since one may want to think of positive things as "actions" (and you cannot possibly change things like os and impl :-D).
But flags and package-names (like e.g. optparse-applicative) are simply variables whose value is constrained by the contents of the cabal file (like build-depends) and user settings on the command line and whose value is determined by the constraint solver. I do not see a fundamental difference (at least from the logical side) between flags and package-names (and frankly, not either from the UX side).
One could translate
library flag: someflaginto
library if !flag(someflag) fail: "inconsistency"
So the double-negation of a flag-proposition is allowed, but not the flag-proposition itself...
But this is not exactly the same. I'm worried that
flag: someflagwill be misunderstood.
It is logically equivalent, but with the new fail you get a specific error message. (This is maybe a general mechanism of giving user-defined errors, but orthogonal to the current issue.)
The current translation is this (or logically equivalent):
library
if !flag(someflag)
build-depends: base<0
which is quite some obfuscation.
If flag: foo is confusing one could have set-flag: foo.
if os(windows)
set-flag: _regex-posix-clib
Yes. Maybe. I once again forgot to not participate in Cabal development. Have fun! (unsubscribing)
(EDIT: to clarify, and don't make decisions on Cabal anymore, so feel free to ignore my comments)