[RFC] Qualified module renamings

Edward Z Yang ezyang at mit.edu
Sun Feb 28 19:37:38 UTC 2021

I agree that transitive dependency is a problem, and I suppose one that cannot even be fixed by sdist elaboration.

As a partial fix (which I believe would be acceptable for Edward) What if reexport wildcards were only permitted for modules that came from the local package? Since we have internal libraries, one might reasonably define a bunch of modules, and then in the same cabal file, want to reexport them from another internal library. If we take care in writing the error message, this restriction may not even be too hard for users to understand. I suppose if we take the theme from your suggestion, which is Cabal files as honest to goodness programs, this would be equivalent to being able to define module lists and then reuse them elsewhere (perhaps after some string processing; similar to common stanzas).

As a more general thought: I noticed in a few other situations where traditional PVP does a poor job at addressing situations where API stability depends on packages you yourself depend on. For example, let's say I wanted to make a metapackage that took a few packages and reexported their modules. You don't even have to talk about the module level: at the declaration level, what identifiers are available from this package will depend on the version choices of its dependencies. But it's infeasible to publish combinatorially many versions of the metapackage for each possible version of its dependency; instead, you'd rather just say that the API of this package depends not only on the package itself, but also some other subpackages. Without some expressivity like this, you just can't write packages like this.


From: cabal-devel <cabal-devel-bounces at haskell.org> on behalf of Oleg Grenrus <oleg.grenrus at iki.fi>
Sent: Thursday, February 25, 2021 2:01 PM
To: cabal-devel at haskell.org
Subject: Re: [RFC] Qualified module renamings

How this will work with packages adding modules.

For concrete example:

- parsec-backpack-0.1 provides two modules Parsec.Prim and Parsec.Combinators
- in my library, I instantiate Parsec.* to Parsec.Text.*, and Parsec.String.*, I get four modules. That is nice.
- parsec-backpack-0.1.1 is released adding Parsec.Error. I get six modules. That is still fine as they are not exported.

However, if the same example is done for re-exported modules, my library public interface would change depending on (minor) version of a dependency. This is no go in Hackage context.

As far as I can see it, reexported-modules and wildcards are no go.

Something like

    reexported-modules: [ [ Parsec.Text.m, Parsec.String.m] | m <- [ Prim, Combinators ] ]


    reexported-modules: for m in [Prim, Combinators]: Parsec.Text.m, Parsec.String.m

or some variation of that idea which fits cabal/module syntax would be both explicit and still more convenient.

I also think it will be even easier to implement, as you all information is there, you don't need to know any context to expand it.

- Oleg

On 25.2.2021 23.01, Edward Z Yang wrote:

> It took me about five minutes to arrive at the guess that this is about the syntax in Cabal files for using backpack - is that right?

Oops yes, sorry for omitting this context.

> What is the intent of what got implemented, anyway? Are there example use cases?

I'm not exactly sure what you mean by intent. But a common pattern in Backpack is to instantiate a library multiple time with different requirements, and if you want them all in scope you have to rename them. Right now, this has to be done one-by-one for each provided module, which can be a bit annoying. For example, let say you parametrized parsec by string type, and you wanted a bytestring version and a text version, it would be convenient to be able to unconditionally rename every Text.Parsec.* module to Text.Parsec.*.ByteString (for example)

Edward Kmett described a concrete motivating use case at https://github.com/haskell/cabal/issues/7290#issuecomment-783540208? although his use case is a little difficult to understand.


From: Bryan Richter <b at chreekat.net><mailto:b at chreekat.net>
Sent: Thursday, February 25, 2021 10:06 AM
To: Edward Z Yang
Cc: cabal-devel at haskell.org<mailto:cabal-devel at haskell.org>; ekmett at gmail.com<mailto:ekmett at gmail.com>
Subject: Re: [RFC] Qualified module renamings

It took me about five minutes to arrive at the guess that this is about the syntax in Cabal files for using backpack - is that right?

What is the intent of what got implemented, anyway? Are there example use cases?

Den tors 25 feb. 2021 18:14Edward Z Yang <ezyang at mit.edu<mailto:ezyang at mit.edu>> skrev:

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.

https://github.com/haskell/cabal/pull/7303 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 (https://github.com/ghc-proposals/ghc-proposals/pull/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 https://github.com/haskell/cabal/issues/7290#issue-812744575?

Thanks Oleg for reminding me to send this RFC to this mailing list.



cabal-devel mailing list
cabal-devel at haskell.org<mailto:cabal-devel at haskell.org>

cabal-devel mailing list
cabal-devel at haskell.org<mailto:cabal-devel at haskell.org>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/cabal-devel/attachments/20210228/eb446161/attachment.html>

More information about the cabal-devel mailing list