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

Reply via email to