-
Notifications
You must be signed in to change notification settings - Fork 710
[POC] Support qualified module renamings #7303
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
base: master
Are you sure you want to change the base?
Conversation
Qualified module renamings let you bring all modules from a library into scope, but qualified under some module prefix. If you write 'pkg qualified Prefix', then if pkg exposes A and B, you will be able to access them as Prefix.A and Prefix.B. This functionality doesn't require any GHC changes; Cabal takes care of desugaring the qualified syntax into an explicit list of renamings. Partially address #7290 Signed-off-by: Edward Z. Yang <[email protected]>
This probably needs a version guard somewhere |
And it also needs docs |
LGTM. I'd like a more general globbing solution, but won't let the perfect be the enemy of 'hey we could ship this and improve the situation' |
@phadej I'm not exactly sure how I'm supposed to operate the Described regex machinery. Currently, IncludeRenaming is specified as:
In particular, the space before requires is optional. This is currently unambiguous because prior to this PR However, I can't just modify the RESpaces to be RESpaces1, because the regular expressions are also being used to recognize valid strings, and it is permitted to omit the space between a trailing parentheses and requires. So I could fix this by sort of just reimplementing the IncludeRenaming regex from scratch, inlining ModuleRenaming into it as necessary to resolve the ambiguity, but this seems very unsatisfactory. Is there an intended recipe for resolving this situation? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be guarded by cabal-version
checks. We cannot extend syntax of old specs after they are allowed on Hackage.
Re regexes. I'd write regex from scratch. Maybe having two braches, one with parens and once without. |
@@ -121,6 +130,11 @@ moduleRenamingParsec bp mn = | |||
P.spaces -- space isn't strictly required as next is an open paren | |||
hides <- bp (P.sepBy mn cma) | |||
return (HidingRenaming hides) | |||
parseQualified = do | |||
_ <- P.string "qualified" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is wrong. It would allow qualifiedBlabla
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some day I will remember that Cabal's parser doesn't actually have a lexer stage before hand 🤣
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nor parsec
has auto-try
. (But Described
generative tests should catch that)
I'd ask you to discuss proposed syntax publicly, at least on I'm feel uncomfortable you jumped straight into implementation. (Though it's good to try if an idea is even viable). |
@phadej Sorry, the intention wasn't to ram this PR through as is without discussion, I wanted to show ekmett that the change he wanted was easy to do (I renamed the subject to add POC to make this clearer). The cabal-devel mailing list seems pretty moribund these days but I'm happy to send an email there. |
Signed-off-by: Edward Z. Yang <[email protected]>
@@ -414,10 +414,28 @@ instance Described ForeignLibType where | |||
describe _ = REUnion ["native-shared","native-static"] | |||
|
|||
instance Described IncludeRenaming where | |||
describe _ = mr <> REOpt (RESpaces <> "requires" <> RESpaces1 <> mr) | |||
-- Unfortunately, we can't directly use ModuleRenaming, as | |||
-- there is some ambiguity in the grammar that we can't express |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this an expressivity problem of a framework, or is there an actual ambiguity in the grammar? I might sound like a SPJ, but can you write down the BNF-grammar then (assuming there is munching lexer, so without need to worry about spaces?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note, these regexes are virtually only documentation of backpack field syntax (re: #4761).
And while mixin
syntax definition in https://cabal.readthedocs.io/en/3.4/buildinfo-fields-reference.html is technically correct, it goes beyond what I'd consider "human understandable".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this an expressivity problem of a framework, or is there an actual ambiguity in the grammar?
Ooh, I used a bad turn of phrase here. Ambiguity isn't the right word.
First, here is the EBNF you requested:
IncludeRenaming ::= ModuleRenaming { "requires" ModuleRenaming }
ModuleRenaming ::=
(* empty *)
| "(" Renaming ")"
| "hiding" "(" ModuleList ")"
| "qualified" ModuleName
RenamingList ::=
Renaming
| Renaming "," RenamingList
Renaming ::=
ModuleName
| ModuleName "as" ModuleName
ModuleList ::=
ModuleName
| ModuleName "," ModuleList
OK, now to explain the problem here. The problem I am having with Distribution.Described lies solely in whitespace handling, as I mentioned in #7303 (comment) The problem is that it is only valid to omit the space before requires (RESpaces) if you didn't select the "qualified" production in ModuleRenaming. If you assume that everything is tokenized beforehand, the problem disappears because M.Nrequires
will properly tokenize as a single module name, while )requires
will tokenize as two tokens.
Note, these regexes are virtually only documentation of backpack field syntax (re: #4761).
Yes, no excuse for this. I guess now is a good time to nail that issue :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@phadej Backpack docs exist and landed!
How would this proposed syntax work with reexported-modules and signatures? |
So, the second part (not included in this PR) is an extension to reexported-modules to permit globbing of reexports (this is not relevant to signatures as we don't require you to write all the requirements in a package, we'll infer them). But this is substantially more fraught because this is tantamount to doing #5343 which means that we have to shave the "update cabal file when you sdist" yak first. |
Signed-off-by: Edward Z. Yang <[email protected]>
Marking this PR as draft 🙂 |
Hello, I am going through old PRs to check whether they are stale. If this PR is still “live”, write a comment and I will remove the |
Summary
Qualified module renamings let you bring all modules from a library into scope, but qualified under some module prefix.
Instead of something wordy like:
You can write this compactly as:
Partially address #7290
Description
Today, using the 'mixins' field you can rename modules that come from other packages by manually expressing a renaming one-by-one. In some Backpack use cases, you may have a lot of modules that you would like to mechanically rename into some subnamespace; today, you have manually list each renaming one by one.
This PR contains an implementation of one possible way to extend mixin syntax to support qualified renaming; the implementation is very simple. The syntax here is based off of Richard Eisenberg's local modules proposal (ghc-proposals/ghc-proposals#283) which supports the qualified keyword before module exports/imports which has the same effect (bring the module into scope under a sub-module namespace). However, the PR isn't really meant to be an end all to the discussion: it's just to show that it's pretty simple to implement this functionality.
There are two primary axes which I am looking for feedback:
Expressivity. The current PoC implementation only permits unconditionally prefix-ing all modules that would have been brought into scope by the mixin; e.g., transforming module A to Prefix.A. Edward Kmett has expressed that in some cases, he would like it if you could implement the import as a suffix. One could also imagine allowing arbitrary string transformations. Opinions on where to draw the line for expressivity are solicited.
Syntax. The current syntax is "pkgname qualified Prefix" as it is symmetric with "pkgname hiding (A, B)" and it was simple to implement. But I am not particularly attached to this syntax, and am open to other suggestions. If we permit suffixing, a wildcard based syntax like "pkgname (* as *.Suffix)" may be preferable (though modestly more complex to specify and implement; for example, is the glob recursive over dots?). Edward Kmett has offered some other possibilities at Globbing support for mixins and rexported-modules #7290 (comment)
Please include the following checklist in your PR:
Please also shortly describe how you tested your change. Bonus points for added tests!