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

What is the semantics of importing a top-level module from a sub-module? #38011

Closed
jonathan-laurent opened this issue Oct 13, 2020 · 7 comments · Fixed by #38271
Closed

What is the semantics of importing a top-level module from a sub-module? #38011

jonathan-laurent opened this issue Oct 13, 2020 · 7 comments · Fixed by #38271

Comments

@jonathan-laurent
Copy link

jonathan-laurent commented Oct 13, 2020

It is well documented how to use relative paths to import individual names that belong to a parent or sibling module.
However, the following code seems to be valid in Julia:

module TestPackage
  export x, y
  x = 0
  module Sub
    using TestPackage  # or alternatively `using ..TestPackage`
    z = x
    # z = y # error (as expected)
  end
  y = 1
end

Here, it appears that the effect of having using TestPackage in Sub is to bring all exported names from TestPackage that are defined before Sub into scope. In the example above, this means that x is accessible in Sub but y is not.

Is this behavior documented somewhere though and would it be safe to rely on it?

@KristofferC
Copy link
Member

It brings all exported symbols in but you haven't defined y yet so how could it not error?

@jonathan-laurent
Copy link
Author

@KristofferC Sorry, I did not formulate my question well. It is perfectly expected that the code will result in an error if you uncomment the line # z = y in Sub.

My question was about the semantics of using TestPackage in the following code snippet:

module TestPackage
  export x, y
  x = 0
  module Sub
    using TestPackage  # or alternatively `using ..TestPackage`
    z = x
  end
  y = 1
end

My hypothesis is that it brings all exported symbols that are defined in TestPackage before Sub into scope. But I did not see this documented anywhere and I am unsure whether or not I can rely on it.

@KristofferC
Copy link
Member

My hypothesis is that it brings all exported symbols that are defined in TestPackage before Sub into scope.

How did this hypothesis form? Some minimal testing doesn't seem to agree with that:

julia> module TestPackage
         export x, y
         x = 0
         module Sub
           using ..TestPackage  # or alternatively `using ..TestPackage`
           f() = y
         end
         y = 1
       end
Main.TestPackage

julia> TestPackage.Sub.f()
1

@jonathan-laurent
Copy link
Author

Interesting. I did not test this exact scenario.
What I tested is that the following yields an error:

module TestPackage
  export x, y
  x = 0
  module Sub
    using ..TestPackage
    z = y # error!
  end
  y = 1
end

But all of this makes my question all the more relevant.
What is the semantics of using ..TestPackage in Sub? Is it safe to rely on it? Or is it an undocumented anti-pattern that may result in an error in the future? What makes your example snippet work whereas mine does not (I could formulate some hypotheses here again but I am wondering what the general rule is).

@KristofferC
Copy link
Member

What makes your example snippet work whereas mine does not

You are trying to use y before it is defined so it errors, I use it after y has been defined.

@jonathan-laurent
Copy link
Author

Based on your example and on my knowledge of Julia's internals, I think I understand what using ..TestPackage does in this example. Thanks for your help.

But I think you would be wrong to think that this behavior is self-evident and not worth documenting better. I know languages in which such a "recursive" import would be forbidden, and Julia could have plausibly been one of them. Also, I know languages where it is allowed but where the snippet you shared would be rejected (as well as languages in which the rejected snippet that I shared would be accepted).

Finally, a question remains: is it safe to rely on the pattern I described or is it an undocumented anti-pattern that may result in an error in the future? To me, there is no way to answer this question based on the available documentation and my understanding of Julia's internals.

I am happy to help improving the documentation on these matters if people deem it useful.

@KristofferC
Copy link
Member

I am happy to help improving the documentation on these matters if people deem it useful.

That's great. I agree it is not obvious what such a recursive import would mean so more docs for that would be useful, indeed.

tpapp added a commit to tpapp/julia that referenced this issue Nov 2, 2020
Main changes

Namespaces, export, import, using

- explain namespaces, qualified symbol names, and export lists
- consolidate examples where applicable
- fix examples which were outdated after JuliaLang#25306
- distribute the “Namespace miscellanea” section into parts in the appropriate sections
- break up the summary table and explain each case in detail with examples
- subsection on various strategies to handle symbol conflicts
- use “bring into the namespace/scope” instead of “import” in the text, as readers confuse it with `import`
- explicitly document what happens with multiple `using` / `import` statements
- add relevant style / best practices suggestions which are now widely used in the Julia ecosystem

Submodules

- discuss usage of `using ParentModule.SubModule` etc (relies on code loading)
- add submodule examples where order matters, fixes JuliaLang#38011 (also another example by @rdeits [from Discourse](https://discourse.julialang.org/t/problem-with-using-in-submodules/42321/2))
- mention that submodules do not “inherit” scope from parent

Misc incidental changes

- in the Methods chapter, add a note about adding methods to functions in other modules
- add a markdown id to the code-loading chapter

The “Module initialization and precompilation” section is left unchanged.
timholy added a commit that referenced this issue Nov 5, 2020
* Reorganize the Modules chapter of the manual.

Main changes

Namespaces, export, import, using

- explain namespaces, qualified symbol names, and export lists
- consolidate examples where applicable
- fix examples which were outdated after #25306
- distribute the “Namespace miscellanea” section into parts in the appropriate sections
- break up the summary table and explain each case in detail with examples
- subsection on various strategies to handle symbol conflicts
- use “bring into the namespace/scope” instead of “import” in the text, as readers confuse it with `import`
- explicitly document what happens with multiple `using` / `import` statements
- add relevant style / best practices suggestions which are now widely used in the Julia ecosystem

Submodules

- discuss usage of `using ParentModule.SubModule` etc (relies on code loading)
- add submodule examples where order matters, fixes #38011 (also another example by @rdeits [from Discourse](https://discourse.julialang.org/t/problem-with-using-in-submodules/42321/2))
- mention that submodules do not “inherit” scope from parent

Misc incidental changes

- in the Methods chapter, add a note about adding methods to functions in other modules
- add a markdown id to the code-loading chapter

The “Module initialization and precompilation” section is left unchanged.

Co-authored-by: Tim Holy <[email protected]>
Co-authored-by: Simon Byrne <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants