JuliaLang / julia

The Julia Programming Language
https://julialang.org/
MIT License
45.68k stars 5.48k forks source link

Only make exported bindings available with `import` #12069

Open mauro3 opened 9 years ago

mauro3 commented 9 years ago

My (maybe wrong) understanding is that the public API of a package is what its main module exports.

However, exports only impact when using a Package and not when importing. However, for larger scale software projects it is probably often preferable to use import to get the safety benefits of separate name-spaces. But this means that when I program in the "safer" import-mode there is no easy way to tell whether I use the public API or some private feature. So, it's not really safer at all.

I wonder whether it would make sense and be more consistent with using that import only allows access to the exported bindings, and that importall imports all the bindings (and also allows method extension, i.e. as it is now):

module A
export a
a() = 1
b() = 2
end
# Defining a module in-line does an implicit
#import A

# now only exported symbols are available
A.a() # works
A.b() # ERROR: UndefVarError: A.b not defined

importall A
A.b() # now works
A.a(x) = 5 # can now be extended
A.b(x) = 5 # can also be extended

(this needs more working out though)

This would be a breaking change but could maybe be done through depreciation warnings. Worth the hassle though?

Or could/should one write a @safeimport macro doing above?

Related: #8000.

vtjnash commented 9 years ago

there seems to be a mixing of terms here. importall is explicitly doing the import of absolutely everything. it's target usage is really interactive development, where you don't really care about safety or conventions, but just want to try out an idea quickly. using makes the exports available. import is the safe option for listing exactly what you want.

mauro3 commented 9 years ago

No, I'm arguing that there should be a construct which only makes the exported variables available through the fully qualified dot-syntax. Reiterating above example, package A defined like so:

module A
export a
a() = 1
b() = 2
end

now using this with, let's call it import_only_exported for the sake of clarity:

import_only_exported A

# now only exported symbols are available with fully qualified syntax
A.a() # works
A.b() # ERROR: UndefVarError: A.b not defined

# none are introduced to the current workspace:
a() # ERROR: UndefVarError: a not defined
b() # ERROR: UndefVarError: b not defined

But it was maybe a bit confusing as I suggested both import and importall be modified slightly (in an effort to keep the number of keywords constant...).

The idea would be that import_only_exported would allow to easily program with only the public API (i.e. what's exported) without having to pollute your name-space with using. I don't think that is possible at the moment but it may be of value?

nalimilan commented 9 years ago

If field overloading was allowed and .. became the operator for direct field access (https://github.com/JuliaLang/julia/issues/1974), then I'd be in favor of making unexported functions available only using A..b().

But adding yet another variant of import would only create more confusion IMHO.

toivoh commented 9 years ago

The problem exists also when you are doing specific imports, eg

import A.b

works even though b is not exported in A.

I agree that it should be possible to import things in a way that protects you from importing an unexported symbol by mistake, also when using the more disciplined alternatives to using. This is no simple fix though, and I'm not sure what could be a good solution.

toivoh commented 9 years ago

Restricting access to A.b seems to require some kind of access restriction as discussed in #12064, but coming up with a form of import that disallows to import A.b seems more about coming up with the right syntax (which is hard enough, of course :)

mauro3 commented 9 years ago

I like @nalimilan's suggestion, if the .. comes along.

tknopp commented 9 years ago

Would it be possible to chain the private symbols. I.e. that in the initial example b would only be accessible by A.A.b() ? I know that this might currently be a technical problem (since A == A.A) but conceptually this could work

ScottPJones commented 9 years ago

As an alternative to .., which other people have expressed a desire to use for intervals, IIRC, what about .!. I think it is less accident prone (one vs two dots is harder to see), and more visible (exclamation points do grab your attention).?

DemiMarie commented 9 years ago

I definitely want a way to be able to access the exported members – and only the exported members – using the module name as a namespace qualifier. This is to avoid PHP's massive global namespace, which leads to confusion as to what name comes from what module.

DemiMarie commented 8 years ago

Ping?

This seems like a major problem for large-scale projects. Common Lisp solved it by separating a:b (accesses only exported symbols in package a) and a::b (accesses any symbol in package a).

adamryczkowski commented 6 years ago

Can I ask what is the status of this proposal? Is it placed in the roadmap (and in what shape?), rejected or still under discussion?

taqtiqa-mark commented 5 years ago

In case it helps.... the following Discourse comment provides and example of how to create private properties for a type.