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

Re-exporting one module's symbols from another #1986

Closed
dcjones opened this issue Jan 10, 2013 · 45 comments
Closed

Re-exporting one module's symbols from another #1986

dcjones opened this issue Jan 10, 2013 · 45 comments
Labels
design Design of APIs or of the language itself modules speculative Whether the change will be implemented is speculative

Comments

@dcjones
Copy link
Contributor

dcjones commented Jan 10, 2013

It would be convenient if there were a way to do this.

module A
    export lots, of, stuff
    # ...
end

module B
    using A
    # magically export everything that was just imported from A
end

Doing this explicitly (copy the long list of exports from A into B) is a bit ugly and error prone. I imagine any alternative would involve a new keyword though.

Concretely, Gadfly is useless without symbols from Compose (and probably DataFrames), but a bunch of compulsory using statements isn't great.

@dcjones
Copy link
Contributor Author

dcjones commented Jan 11, 2013

Actually, I would propose altering export's semantics to make this work:

module B
    using A
    export names(A)
end

@johnmyleswhite
Copy link
Member

This is a very good point. I've taken to doing using DataFrames at the start of any new package code that has DataFrames as a requirement.

@StefanKarpinski
Copy link
Member

This could be done with a macro, but maybe we should provide some syntax for it.

@stevengj
Copy link
Member

stevengj commented Aug 7, 2013

My suggestion would be exportall A, which would re-export the exported names of A (but not the unexported names!).

@vtjnash
Copy link
Member

vtjnash commented Aug 7, 2013

A potential downside is that it may make discovery of exported names a bit harder. Also, I'm disinclined to support reserving another name exportall. So, alternatively, something like: using A as B with reexport perhaps? Or export A. (a module name followed by a period)?

@stevengj
Copy link
Member

stevengj commented Aug 7, 2013

using A as B with reexport seems weird to me (and if you are willing to add a reexport keyword, why not reexport A?), but export A. seems fine to me if a bit subtle.

I don't understand your concern about discovery of exported names; could you clarify?

@vtjnash
Copy link
Member

vtjnash commented Aug 7, 2013

Currently, it easy to parse any julia file, search for export at the start of an expression, and interpret the comma-separated list that follows to quickly identify the public interface for a module.

using ... as ... with reexport adds special syntax to using, not a new keyword

@stevengj
Copy link
Member

stevengj commented Aug 7, 2013

I see, you are worried about manually parsing for exports. It's not too bad with reexport, though—you just need to do the same thing recursively for each reexport.

If you are wedded to using, I would tend to just support using A reexported.

@vtjnash
Copy link
Member

vtjnash commented Aug 7, 2013

I'm just tossing around other proposals for consideration. Very likely, none of them are inherently better. I didn't mean to imply the as ... part was required. using A rexported or using A reexported as B or using A as B reexported would all be valid. Whether this is a single word or a phrase depends on whether we want to be closer to a english sentence or a keyword. I could accept either.

@staticfloat
Copy link
Member

I personally kind of like exportall. It's got a nice symmetry with importall.

@simonster
Copy link
Member

I made a macro that mostly works for this pupose. I say mostly because relative module syntax does not work. The parser parses @reexport .MyModule as reexport.@MyModule and throws a syntax error on @reexport .MyModule1, .MyModule2. But @reexport MyMod.MySubMod should work.

@jiahao
Copy link
Member

jiahao commented Jan 27, 2014

If we ever get a resolution of this issue, we would be able to remove hundreds of lines' worth of redundant-seeming exports from Base.LinAlg and then again from Base.

simonster added a commit that referenced this issue Jan 30, 2014
Syntax is:

@reexport using MyModule1, MyModule2

Fixes #1986
@kmsquire
Copy link
Member

I'm with @staticfloat, in that I find exportall most appealing.

What's the downside to adding that as a keyword?

@simonster
Copy link
Member

What does exportall do if you haven't imported/used anything from A, e.g.:

module A
    exported = true
    export exported
end

module B
    exportall A
end

Exporting bindings that are not accessible in B is not really an option, so does it throw, export only the A module and none of its exported symbols, or implicitly call using or importall? Unless it does something implicitly, it is more verbose than the alternatives, since you need two lines of code instead of one and you need to repeat the module name(s) twice. And if it does something implicitly, then we are adding a fourth keyword that imports bindings.

@kmsquire
Copy link
Member

What does exportall do if you haven't imported/used anything from A

Throw an error. Even though it's more verbose, I find it clearer and more natural than most of the proposed alternatives.

@simonster
Copy link
Member

If exportall is going to throw if the module isn't already imported/used, it may as well be a macro? Since all imported/used modules will be in the module's scope by virtue of using, it's not clear to me it needs to support relative module paths, which is the only thing a macro couldn't do.

@tknopp
Copy link
Contributor

tknopp commented Jan 31, 2014

Whats the advantage of being a macro? It would be totally inconsistent if we have import, importall, export, @exportall

@simonster
Copy link
Member

Minimalism? It would be one fewer reserved identifier, one fewer Expr head, and in Julia instead of C. It is already a little awkward if it joins end, elseif, and else as (I think) the fourth keyword that throws an error if another keyword has not been used before it, and in this case it's a runtime error instead of a parse error.

I kind of see the symmetry, but it doesn't seem quite right to me: You can add bindings from another module to the current module and you can export bindings in the current module from the current module, but you cannot export bindings in another module from the current module unless those bindings are already also present in the current module.

Personally, I would still prefer #5608 or #5626, which combine the loading and exporting of the bindings into one step. It seems to me that:

using .Windows, .Periodogram, .FFTFilt, .FilterDesign, .Util
exportall Windows, Periodogram, FFTFilt, FilterDesign, Util

is less elegant and easier to screw up than:

@reexport using .Windows, .Periodogram, .FFTFilt, .FilterDesign, .Util

But if everyone else wants exportall and is averse to the @ symbol, I can try to make that happen.

@tknopp
Copy link
Contributor

tknopp commented Jan 31, 2014

Well, minimalism is a point that is always a concern when thinking about adding a keyword.

I think the more general question is whether the regular Julia user should be confronted with the usage of macros. I would say no. This is an advanced thing and should not be part of the regular workflow.

exportall is from my perspective something that will be needed in the "regular use" Its quite common to structure modules (namspaces, ...) into two levels.

@lindahua
Copy link
Contributor

@tknopp: Yes, writing a macro might be an advanced thing that ordinary users should not need to worry about. However, I don't think using a macro is that advanced -- they don't even need to understand how a macro works behind the scene in order to use it. All what we need is to clearly document the macro's usage.

For instance, @inbounds is a good example which everyone can easily understand and use.

Also, a code author should be quite tech savvy when he is worrying about something like re-exporting names.

@tknopp
Copy link
Contributor

tknopp commented Jan 31, 2014

@lindahua: macros are great. But still they do "magical" code transformations that I think most users should not be confronted with. @inbounds is a good example. This is a thing for advanced usage not for regular usage.

I think reexporting names will be not that uncommon in practice. The export mechanism is Julias way for information hiding. And having submodules in modules is common and should also be encouraged.

@pao
Copy link
Member

pao commented Jan 31, 2014

A better example for "macros all users should be comfortable with" is @printf.

@kmsquire
Copy link
Member

A better example for "macros all users should be comfortable with" is @printf

Which I think should be replaced with a pure Julia implementation when we can. (I have one partially implemented.)

@simonster
Copy link
Member

@printf is pure Julia. It's just a macro. While a function could be useful for rare cases where the format string could vary at runtime, I am not sure how a function could be as performant as the macro, since the macro generates code based on the format string at load time.

We also have @__FILE__, which is a keyword in most other languages.

@tknopp
Copy link
Contributor

tknopp commented Jan 31, 2014

Probably @time and @assert are the best examples of macros for which my statement that macros are only for advanced users does not hold.

@papamarkou
Copy link
Contributor

Has there been any further progress, decision or discussion on this matter? Is @simonster 's Reexport package the agreed standard way to re-export the symbols of another package, i.e. should I use it?

@johnmyleswhite
Copy link
Member

Since I haven't seen any other options emerge, I think Reexport is the de facto standard.

@papamarkou
Copy link
Contributor

Thanks @johnmyleswhite :) I will be relying on it and will keep an eye on this thread.

@tknopp
Copy link
Contributor

tknopp commented Dec 23, 2014

It would be really great if we could have this functionality in Base. It would be great to have metapackages that simply group several packages together in order to provide a larger set of functionality.

@StefanKarpinski StefanKarpinski added speculative Whether the change will be implemented is speculative design Design of APIs or of the language itself modules labels Aug 11, 2016
@StefanKarpinski StefanKarpinski added this to the 0.6.0 milestone Aug 11, 2016
@vtjnash vtjnash modified the milestones: 1.0, 0.6.0 Dec 22, 2016
@vtjnash
Copy link
Member

vtjnash commented Dec 22, 2016

Needs some design thoughts to figure out how we want this to work and interact with using / import / importall / etc.

@timholy
Copy link
Member

timholy commented Jan 5, 2017

Crossref #14472

@vtjnash
Copy link
Member

vtjnash commented Jul 13, 2017

I put together a macro implementation of something like this for one of my PRs as a demo: https://github.com/JuliaLang/julia/pull/22147/files#diff-4fc3422b2208cb02c3e17c7cc0a56446R44

@Keno Keno modified the milestones: 1.x, 1.0 Jul 27, 2017
@Keno
Copy link
Member

Keno commented Jul 27, 2017

Seems feature-y. There might be syntax we could want, but we if that does happen we can live with a macro until 2.0.

@ip1981
Copy link

ip1981 commented Oct 31, 2017

Re-export is evil.

@epatters
Copy link

I disagree that Reexport is evil. In writing a package with many levels of submodules, I find Reexport very convenient for managing namespaces across levels without lots of boilerplate code and code duplication. I echo the sentiment expressed above that this would be great functionality to have in Base.

@rapus95
Copy link
Contributor

rapus95 commented Jun 29, 2021

how about introducing the import/using syntax to export aswell? I.e.

using B
export B

for reexporting all exported identifiers from B
That would even allow exporting as something else 🤯 :D
Sure, given that it would be breaking, that would have to wait for Julia 2.0 but it would be so concise IMO
Omitting the leading module name could be interpreted as @MODULE. Then it would work the same as now.

module A
  using B
  export A: B #same
  export B: B #same
  export B #different
end

But given that exporting an imported module w/o exporting its fields doesn't make a lot of sense on its own right now, redefining export B to mean reexport B would actually make sense IMO

@DilumAluthge DilumAluthge removed this from the 1.x milestone Mar 13, 2022
@ViralBShah
Copy link
Member

Is Reexport.jl still the recommended way to do this? Lots of packages are using it, and seems to work fine. If there isn't anything we are planning to do here, we can close this.

@vtjnash vtjnash closed this as completed Feb 7, 2024
@vtjnash
Copy link
Member

vtjnash commented Feb 7, 2024

yes, Reexport.jl seems like an okay way to do this. The names are easy enough to get via reflection and eval into the current module

AntonReinhard added a commit to QEDjl-project/QEDcore.jl that referenced this issue Jul 12, 2024
Add Reexport.jl to the dependencies and use it to reexport QEDbase's
symbols. This is the officially endorsed way to do this
(JuliaLang/julia#1986).

Fixes part of #24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
design Design of APIs or of the language itself modules speculative Whether the change will be implemented is speculative
Projects
None yet
Development

Successfully merging a pull request may close this issue.