*Scott and Michael:* I am pretty certain I understand what you are saying, but I find your examples/descriptions a bit confusing. I think (hope) I know why Stafan is confused.
*Stefan:* I think Scott has a valid point but I disagree that "exported functions from a module must reference types defined in that module". It is my strong belief that Scott is merely focusing on the symptom instead of the cause. Fortunately, I am certain that this is not the crux of the problem... And I agree completely with Stefan: Limiting exports to this particular case is extremely restrictive (and unnecessary). I also agree that, this restriction *would* keep developers from developing very useful monkey patches (among other things). So let's look at the problem differently... *Problem 1: A module "owns" its verbs.* See discussion above discussion ( https://groups.google.com/d/msg/julia-users/sk8Gxq7ws3w/ASFlqZmVwYsJ) if you are not familiar with the idea. *Problem 2: Julia has 2 symbol types (for objects/methods/...)* Well, this is not really a problem... It is terrific! Julia's multi-dispatch engine allows us to overload methods in a way I have never seen before! In fact, the multi-dispatch system allows programmers to DO AWAY WITH namespaces altogether for the new type of symbol (at least to a first order). *So what are the symbol types?* 1) Conventional Type: Used in most other imperative languages 2) Multi-Dispatch Type: Symbols of methods whose signature can be uniquely identified. By this definition, if a symbol is not associated with a unique signature, it is simply a conventional symbol. And, as defined, conventional symbols run a high potential for "signature collisions"... because the signature is insufficient to uniquely identify whether we are referring to Foo.CommonSymbol(???) or Bar.CommonSymbol(???). *So why do we need namespaces?* Namespaces were created to let programmers use succinct symbol names without the problem of running into never-ending name collisions. Instead of dealing with symbol bloat (Foo.FoosSpecialSymbolThatCannotCollideWithAnybody) - we use scopes to give symbols a nice hierarchy (namespaces are basically named scopes). When writing code in the native scope, all symbols are nice and short... and you can even *import* the symbol names to your own scope to interact with the module. Now the user gets to use short names - not just the module developer! *So what about the multi-dispatch-ed symbols (type 2)?* Technically, they could *all* be located at the global scope. You don't really need to say Foo.run... because the call signature is unique (by definition) - so there is no ambiguity. *Ok, then where is the problem for module developers?* Simply put: Julia is trying to cram those beautiful multi-dispatchable methods into a construct (namespaces) that *is not technically needed* for type 2 symbols. As a consequence, the current Julia implementation actually makes it *difficult* for module developers to use multi-dispatch. Of course, everything is just peachy when you work from within Base :). Unfortunately, I believe this has caused a little more collateral damage: Since it is easier to work within Base, it has become this sort of "god module". I believe Jeff has mentioned developers are getting reluctant to make it grow any further. *The Distributions module: Can it remain elegant?* The short answer is yes :)! Stefan has a valid concern: you either have to keep using full symbol paths (Distributions.Normal), or "[...] you'd have to explicitly import every Distributions type and generic stats function that you want to use. Instead of being able to write `using Distributions` and suddenly having all of the stats stuff you might want available easily, you'd have to keep qualifying everything or explicitly importing it. Both suck for interactive usage - and frankly even for non-interactive usage, qualifying or explicitly importing nearly every name you use is a massive and unnecessary hassle. " I agree. We don't want this... what a pain! Let's not go there. *So where do we start?* In this case, "Normal" is a relatively common name, and I cannot say its argument list is sufficiently unique to qualify it as a type 2 method. The signature only involves two "::Number" arguments. Good rule of thumb: If an argument list is made up solely from base types (number/char/string/...) is likely to collide with a function in another module (say, a Geometry module). That was easy!: That means "Normal" is a type 1 symbol. As such, Normal *should* be "export"-ed by module Distributions... Nothing changes in the implementation! *Now how do we generate numbers with a normal distribution?* The Distributions package includes a rand() function. Interestingly enough, the signature for rand makes it a type 2 method - and so it has a very low probability of signature collisions. Here is a simplified definition of the rand function (Not using UnivariateDistribution, ...): import Base: [...] rand rand(x::Normal, n::Int) = ... Basically: Distributions is extending Base.rand With this infrastructure, you can generate random distributions with very nice code: using Distributions X = Normal(0.0, 1.0) values = rand(X, 3) *So what is the problem here?* Again, it is quite subtle: In Julia, the first module to define a method essentially becomes the "owner" (in this case "Base"). This is awkward. *Then what could be done differently?* Well, one possible solution would be simply declare type 2 methods as "global": global rand(x::Normal, n::Int) = ... (tentative syntax) Due to its unique signature, this type 2 method is unlikely to collide with the definition from another module anyways... so why not make it global? NOTE: I don't think this is ideal, but it is much better than the current solution where a module "owns" a given symbol (or verb, if you will). *Ok, but does this actually help?* Surprisingly yes! By not using the same hammer for type 1 & type 2 symbols, *module developers* get a means to push the safe type 2 symbols to the global namespace... and *module users* get to decide when/where to import type 1 symbols from a given library. Consequence 1: It will be very unlikely that two modules will trigger signature collisions. For example: draw(PlotPackage1.Canvas, ...) does not collide with draw(PlotPackage2.Canvas, ...). Consequence 2: Even in any other programming language out there: you expect that type 1 symbol collisions be fatal (because they *are* ambiguous). As usual, you can only import symbols ("using") from *a single* conflicting module to the current namespace at a time. The other import *must* be a true "import". ...But that's ok... because you still get to use Julia's awesome multi-dispatch engine on all the type 2 symbols!!! >>>And that's the *biggest* problem with Julia's current behavior. You don't even get to use multi-dispatch on type 2 symbols when you do an "import". You only get to reap the benefits when "using" succeeds - or if you import individual type 2 methods.<<<
