Currently, we don't have a way to make names private. The convention is to prepend _ to the name. But this is just a _convention_, which users can and do ignore.
Omitting how and why the inability to make names private can ruin everything in large codebases, I propose to add private keyword for making names private that is analogous to export. So something, which currently written as
module Mod
export a, b
a(x) = _a(x)
b(x) = _b(x)
_a(x) = ... # internal implementation
_b(x) = ... # internal implementation
end
would be written as
module Mod
export a, b
private c, d
a(x) = c(x)
b(x) = d(x)
c(x) = ... # internal implementation
d(x) = ... # internal implementation
end
and c and d would not be accessible outside the module. This also makes the code more clean and readable (without hairy names starting with _), which is something that Julia generally strives for. This way we would have 3 kinds of names:
exported)I have no knowledge of compiler, but I would expect that this can also enable some compiler optimizations that otherwise are not possible.
We could also use internal, protect, protected, hide or hidden instead of private.
Related: #12069
An alternative design is to require an explicit global annotation in order for a name to be externally accessible via M.x; that effectively makes private the default. Of course, it would be breaking unless it was opt in.
Another thought I've had is that "private globals" should probably be private to a given file. When you use include to include a lot of files it's a bit easy to accidentally have name collisions across files that are totally unrelated.
An alternative design is to require an explicit
globalannotation in order for a name to be externally accessible viaM.x
That could similarly be accomplished with export M.x, to signify that it's public but the intended API for it is to qualify it with the module name, e.g. CSV.read. Unless you mean that it would not be accessible at all without a global annotation. That would make unit testing internals a bit harder.
Unless you mean that it would not be accessible at all without a
globalannotation. That would make unit testing internals a bit harder.
That was what I meant, but you're right that this would be kind of annoying for testing stuff.
Having a way to access the private bindings would be Julian. In #12069 using a..b was suggested.
Since making binding private has a cost of making things such as unit testing more difficult, I think it should not be the default option. However, I think if the user really, really wants to make a binding private (i.e. inaccessible outside the module) he should be able to do so by explicitly writing private or something.
If you really want to make something private, you already can using let:
```jl
julia> module M
let x = 1
global f() = x
end
end
Main.M
julia> M.f()
1
julia> M.x
ERROR: UndefVarError: x not defined
A very simple approach could be to have a nested module:
module A
module __private
#isolate this stuff
end
#less private stuff
end
In order to increase convenience, one would probably want some macro @import_all_from_module, in order to permit unqualified access to the __private namespace inside of A. Then we'd have
module A
module __private
@import_all_from_module ..
#isolate this stuff
end
@import_all_from_module __private
#less private stuff
end
Re really preventing access: I think this is a very bad idea. We should serve the library user, not the library author. The concrete desires of the caller override the imagination of the callee, at least unless we want to perform optimizations that rely on "hidden internals" being inaccessible (like e.g. pointer_from_objref for bitstypes).
Most helpful comment
If you really want to make something private, you already can using
let:```jl
julia> module M
let x = 1
global f() = x
end
end
Main.M
julia> M.f()
1
julia> M.x
ERROR: UndefVarError: x not defined