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

Add export-like keyword for private names in a module #30204

Open
AzamatB opened this issue Nov 30, 2018 · 10 comments
Open

Add export-like keyword for private names in a module #30204

AzamatB opened this issue Nov 30, 2018 · 10 comments
Labels

Comments

@AzamatB
Copy link
Contributor

AzamatB commented Nov 30, 2018

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:

  • truly public names (i.e. exported)
  • public names that are accessible with module name (default option)
  • private names that are not accessible outside the module

I have no knowledge of compiler, but I would expect that this can also enable some compiler optimizations that otherwise are not possible.

@AzamatB
Copy link
Contributor Author

AzamatB commented Nov 30, 2018

We could also use internal, protect, protected, hide or hidden instead of private.

@JeffBezanson
Copy link
Member

Related: #12069

@StefanKarpinski
Copy link
Member

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.

@StefanKarpinski
Copy link
Member

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.

@ararslan
Copy link
Member

ararslan commented Dec 1, 2018

An alternative design is to require an explicit global annotation in order for a name to be externally accessible via M.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.

@StefanKarpinski
Copy link
Member

Unless you mean that it would not be accessible at all without a global annotation. 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.

@mauro3
Copy link
Contributor

mauro3 commented Dec 2, 2018

Having a way to access the private bindings would be Julian. In #12069 using a..b was suggested.

@AzamatB
Copy link
Contributor Author

AzamatB commented Dec 2, 2018

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.

@StefanKarpinski
Copy link
Member

If you really want to make something private, you already can using let:

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

@chethega
Copy link
Contributor

chethega commented Dec 3, 2018

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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants