Is an explicit "export" a good thing?

I know about import. Thanks.

My point was different: mmm does not export ANYTHING. So using it will not lead to the problem of magically populating the namespace. The whole interface to the package is concentrated into mmm_API, which exports the developer-selected set of symbols. So using it would then be useful in the REPL and to document the interface to users, whereas using mmm: ... would be used in the package code.

If mmm exported things it would be very tempting to invoke using (or perhaps it would be done inadvertently). In this way is import mmm and using mmm are one and the same from the point of view of getting access to symbols.

Getting access to everything that is exported is now explicit, not implicit.

1 Like

But that doesn’t do anything different than what using and import already does.

import mmm doesn’t export anything.

You don’t need a separate module to do that. If you want your imports to be documented in one spot, just do it:

So using it would be in useful in the REPL, and import mmm: ... would be used in package code.

It would be as indirect as using mmm_API vs using mmm, except it would be true for every Julia package so people would immediately know the difference by looking at your code, whereas mmm vs mmm_API would be something only you do.

No they aren’t. using is different than import. using allows things to be implicit, import makes it all be explicit. Using two separate modules with your own weird naming scheme to recreate what already exists in the language to do this only obfuscates the intention. There is not a single functional difference between your two module + know which one to using vs smartly choosing between using and import, other than the fact that the latter is standard across the whole language. I don’t get why you would not want to use the already existing standard to do exactly what you’re trying to do in less typing and with more documentation.

I don’t think you understand: mmm does not export anything. Hence it cannot be brought into scope implicitly for resolution of symbols. THAT is my point: import and using of mmm have the same effect. If the user wants implicit access, then it needs to be done by using a different module. The intent is then obvious.

I think here we are talking past each other: what you mean is what happens when someone accesses an already written module mmm. Then obviously there may be some exports already there. In that case you have the option of controlling access with either import or using.

What I mean is the point in time when the module mmm gets written. I’m saying that one can control the access to symbols from that module explicitly by not putting any exports in that module. For implicit access to the symbols that the module developer thinks are public is then through another module.

Maybe I don’t understand what are you talking about but this works:

julia> import .Package
julia> Package.__Inner.other_private(1)
1

and this too:

julia> import .Package: __Inner.other_private
julia> other_private(1)
1

But after importing Package you can still call Package.__Ineer.anything, right?

I don’t see the advantage of this compared to having a single module and import/using. Maintaining two lists of symbols violated DRY and puts an extra burden on the developer, merely to avoid using a mechanism that is already in the language. Perhaps I missed something.

I think it could be good if Julia has some kind of private declaration which could specify this more explicitly.

(maybe unstable for something usable but not guaranteed in next version)

Actually, the developer only needs to write one module, mmm, not to use any exports, and to describe which functions constitute the public interface of the module in the documentation at the top of the module.

The user (if they so desire) can write the module that explicitly makes some functions from mmm available for implicit access with using. For instance, if I use module mmm from the REPL all the time, I might wish to write the very brief module mmm_API that makes everything that I need available with one command.

So, as you can see I would tend to advocate for writers of library modules not to export.

I am not sure how this is better than simply

import ThatModule: thisfunction, thatfunction, etc

If it is always the same list, a module would save some work, but at the same time it also adds overhead, and generally it loses the specificity that comes from a tailored import approach (I may use a specific subset for a project, and import allows me to be explicit about it).

I guess one could put the difference succinctly as who decides what is made available for implicit access:
(1) the developer, or (2) the user.

In the first case, the module mmm exports certain symbols. In the second case, the user is free to write a module that provides that implicit access, but using the module mmm itself does not lead to the problem of magically appearing symbols.

I am not sure I understand; with an explicit import list it is also the user who decides.

Maintaining a separate package-specific API module for some other package looks cumbersome, but perhaps this is because I have not seen it in practice. Can you link an example of a package that employs this technique?

You are absolutely correct, that is of course possible. My point was that if the user wished to do using to get the entire API available at the REPL, employing a separate module to do that is in my opinion preferable to the developer exporting the stuff once and for all.

BTW: I am not trying to persuade anyone to do this. I would like to figure this out for my own package: no obligation for anyone else. :wink:

Here is the MWE:

module mmm  # DEVELOPER WRITTEN: note NO explicit exports
"""
The "exported" function from this module is `publicfun`.
"""
function publicfun()
    println("in publicfun()")
end
function _privatefun()
    println("in _privatefun()")
end
end
module mmm_API # USER WRITTEN
# As one of the users who use `mmm` a lot from the REPL, I want to do `using mmm_API` 
# to get implicit access to both the public function 
# and the private function from the module `mmm`.
using mmm: publicfun, _privatefun
export publicfun, _privatefun
end

Yes, of course, but it is easier too see what is intended to be externally used and what is considered private.

Just in case someone is interested: I figured out how to deal with exports for my package. It addresses the problem of control of conflicting exports by the USER of the package (instead of the DEVELOPER). Should you be interested, drop me a message. Or, have a look at GitHub - PetrKryslUCSD/FinEtools.jl: Finite Element tools in Julia

2 Likes

It would be great if you could provide a brief summary here. Or perhaps a blog post.

2 Likes

I hear you: I am working on it.

2 Likes

The design was described in the FinEtools documentation (https://petrkryslucsd.github.io/FinEtools.jl/modules.html), and in the use-case package (https://github.com/PetrKryslUCSD/FinEtoolsUseCase) which details two methods in which the control of the public interface of FinEtools can be exercised by the USER of the package.

The key is to concentrate all exporting into the top-level file of the package.

I would appreciate any and all constructive comments!

1 Like