Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Constraint language: add a positive way to automatically enable flags #7293

Open
andreasabel opened this issue Feb 22, 2021 · 6 comments
Open
Labels
Cabal: file format re: flag Concerning user-defined flags in cabal files type: enhancement

Comments

@andreasabel
Copy link
Member

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

@phadej
Copy link
Collaborator

phadej commented Feb 22, 2021

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".

@hvr
Copy link
Member

hvr commented Feb 22, 2021

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)

@andreasabel
Copy link
Member Author

@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).

@phadej
Copy link
Collaborator

phadej commented Feb 22, 2021

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.

@andreasabel
Copy link
Member Author

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: someflag

into

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: someflag will 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

@phadej
Copy link
Collaborator

phadej commented Feb 22, 2021

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Cabal: file format re: flag Concerning user-defined flags in cabal files type: enhancement
Projects
None yet
Development

No branches or pull requests

4 participants