I've been trying to learn a bit about Julia's internals in the hopes of understanding enough to be able to make some sensible suggestions on module semantics. So far I've been buried in libuv land as a learning exercise... https://github.com/JuliaLang/julia/pull/9450.
It occurs to me is that it's nice to have a simple succinct semantic that is immediately understandable, and to make sure that any convenience syntax (or macros) that sit on top of that have no hidden magic. (I come from an odd place called Tcl where there are only 12 rules http://wiki.tcl.tk/10259) Please excuse me if any of the following is obvious to those in the know, or is silly in the context of Julia... these ideas are not fully thought out yet... e.g. what if the only way to call a function in a different module was by it's full name mymodule.f() and there is no using or import. If I want to use f() from mymodule in the current module I can just do f(x) = mymodule.f(x). If I already have a function f(), then the semantics of how the mymodule.f()'s methods get merged in are clearly defined by the existing dispatch rules, no special module rules are needed. On top of that you could have functions or macros for "using" and "import", but they would just be for convenience, no new rules. An example I've considered is a GUI toolkit or a plotting library where you have possibly hundreds of functions and you don't want to be saying FooBarGraphicsLibrary.display(my_window) all over the place, you'd rather just say display(my_window). However, there is a problem with possible unexpected dispatch results if you just splat all of FooBarGraphicsLibrary's functions/methods into the current scope. Maybe a good pattern is to have a macro that says @prefix "fbg_" "FooBarGraphicsLibrary.* that does fbg_display(x...) = FooBarGraphicsLibrary.display(x...) etc.. for all the functions in the module. That way all the GUI toolkit functions names have an unambiguous name prefix but they are not as long and unwieldy as using the full path name. I think that importing large utility APIs is a very different use case to e.g. importing a mathematical type API. If you import an API for imaginary numbers, you want all the usual math functions to just work by the magic of multiple dispatch. However, with something like a web-services API, or any number of other system-programmng API's you probably want to avoid ambiguous dispatch possibilities as much as possible and make it clear to the reader of the code that "this is a call into wrapper library foo". There seems to be a tradeoff between on one hand having dynamically loadable modules that are independently QAed and are guaranteed to have certain behaviour; and on the other hand being able to import modules in a way that gives rich interaction of types and dispatch (and the possibility of full system optimisation etc...). Maybe these two use cases should have different rules...