Names that are not exported from a package can also be imported into another project

I’m encountered with a fairly strange circumstance where the names that are not exported from a package can also be imported into another project environment.

Detail: There is a package named MyPkg that is just in development. In this package, function f has been exported explicitly with export f, while function g hasn’t. I added this package in another project environment with command dev path/to/MyPkg for the purpose of package test. After using MyPkg in the new environment I found I can invoke not only function f but also g.

Is this reasonable?

Thanks in advance!

That is as expected. Symbols from modules can be brought into scope whether exported or not.

That is not expected. If g is not exported, then using MyPkg shouldn’t make the name available. You can still import MyPkg: g to request the internal name explicitly though.

4 Likes

That’s what I meant. Thanks for the clarification.

1 Like

In that case, I need to check what went wrong. :thinking:

Thanks!

I found that the issue lies in my use of Reexport. Once I use the @reexport using SomePkg command, the aforementioned issue occurs. Is this considered a bug?

We use Reexport.jl too, and don’t see this issue.

For instance,

module Y
export g
    g(x) = x + 1
end

module X
export h
    using Reexport
    @reexport using JuMP
    @reexport using ..Y
    add_variable(model) = @variable(model)
    f(x) = x + 2
    h(x) = x + 3
end

using ..X
g(1) # Available 
f(1) # Not available 
h(1) # Available 
add_variable # Available. Why?

What is strange in that example? X directly exports h and reexports g from Y and add_variable from JuMP. It doesn’t export f.

It all looks consistent with your observations at the end.

2 Likes
module Y
export g
    g(x) = x + 1
end

module X
export h
    using Reexport
    @reexport using JuMP
    @reexport using ..Y
    add_variable(model) = @variable(model)
    function add_variable2(model)
        return @variable(model)
    end
    f(x) = x + 2
    h(x) = x + 3
end

using ..X
g(1) # Available 
f(1) # Not available
h(1) # Available 
add_variable # Available. Why?
add_variable2 # Not available. Why?

Maybe what I feel strange lies in I don’t understand the difference between the definitions of function add_variable and add_variable2.

The difference is that JuMP exports add_variable but not add_variable2 and you do @reexport using JuMP.

1 Like

Why does JuMP export add_variable? Isn’t it a function name like add_variable2 that is defined in module X?

Because add_variable is part of the JuMP API? You probably shouldn’t be reexporting JuMP if you don’t know what it contains.

3 Likes

Ah, I see! It had been troubling me for a while. Thank you for your patient help and valuable advice! :handshake: :handshake: :handshake:

But, sorry. There is still a phenomenon that I feel strange.

module X
export h
    using Reexport
    @reexport using JuMP
    add_variable(x) = x + 1
    f(x) = x + 2
    h(x) = x + 3
end

using ..X
f(1) # Not available
h(1) # Available 
add_variable # Available. 
add_variable(1) # Available. Why?

Since add_variable is part of the JuMP API, it’s not surprising that it can be exported. However, I am now wondering why the new methods defined for add_variable in module X can also be exported, and it seems like it overwrite the original add_variable function in JuMP. :thinking:

The reexport macro expands to, among other things,

export add_variable

I.e. module X exports the symbol add_variable, whatever it happens to be bound to in the module. In this case it is first imported from JuMP with using, then you shadow it with your own definition.

1 Like

Thank you, for me it finally makes sense, although I still feels a bit strange. :handshake:

By the way,

module Y
export f, g
    f(x) = x + 1
    g(x) = x + 2
end

module X
export h
    using Reexport
    @reexport using ..Y: f
    h(x) = f(x) + 3
    # Place-1: let `f` available here.
end

using ..X
# Place-2: let `g` available and `f` not available here.

If I want to let f available at Place-1 and let g available and f not available at Place-2, how should I code?

If I understand correctly, you want to use Y.f as f only from within X, and you want to use X.g as g outside of f. If so, then just don’t re-export. Reexport is a convenience for exporting everything. If that’s not want you want, then explicitly export what you do want.

From GitHub - simonster/Reexport.jl: Julia macro for re-exporting one module from another (with emphasis added):

Maybe you have a module X that depends on module Y and you want using X to pull in all of the symbols from Y .

1 Like

Thanks!