Julia is now my favorite language by a wide margin, and aside from the minor quibble of pancakecaseidentifiers, i can hardly fault it.
There is one thing which does bother me however, and that is modules, import/export and visibility. I read a a thread here a while back while I was still a bit of a noob, which got a bit heated on the subject, but wanted to see if perhaps with time I might gain some perspective.
There were two general “camps” in the discussion, those who wanted
data hiding for software engineering reasons, and those opposed where the specter of Java privacy hell loomed large. I was somewhere in the middle.
The first thing which struck me coming to Julia was the verbosity of export lists, considering that you can just import X
and completely override all of it. The second was that being able to do so was not at all POLS, and in my experience quite idiosyncratic to Julia. Because we have a REPL with tab completion the problem is exacerbated, when I write Module.<tab>
, I want to see the public API not the implementation details. I’d like to be able to tell at a glance (i.e. a <tab>
) that which I should be using and that which might disappear out from under me at the next release. Pouring over export lists is not “a glance”, but even if it were, Julia culture (e.g. Base
) is such that some public API functions (convert, promote, etc) are not exported.
Julia is a “freedom” language though, I get it, we don’t want Java hell, however I think that a coarse grained module level overridable privacy mechanism would be a very good thing.
There are several possible routes here but I would like to propose a very simple one. Everything not exported is module private and does not show up on <tab>
. Functions and objects not exported are assumed to be implementation details and are not to be trusted from an API consumption point of view, however any object is accessible via an explicit extract
statement.
Something like:
# file: M.jl
module M
export foo
foo() = :foo
bar() = :bar
end
# file: importM.jl
import M
M.foo() # :foo
M.bar() # throws error
# file: extractM.jl
extract M
M.bar() # :bar
typeof(M) # ExtractedModule
extract M: bar
bar() # :bar
where: Module <: AbstractModule && ExtractedModule <: AbstractModule && Base isa ExtractedModule
, and where using
would remain unchanged, excepting in the form using M: bar
where extract would have to be used instead.
I am cognizant that this proposal might be a really bad one for a number of technical reasons unbeknownst to me, and know that 0.7 is nearing, but am hoping that this issue might have some resolution, or even just one last good look/discussion before that occurs.