renaming loadModule()/importModule() (was: import[From]Module removal)

30 views
Skip to first unread message

Hannes Wallnoefer

unread,
Oct 2, 2008, 4:40:44 AM10/2/08
to helm...@googlegroups.com
Hi Chris,

2008/10/2 Christian Langreiter <ch...@langreiter.com>

Dear Helmatics,

is the reasoning behind removing import[From]Module documented/
summarized somewhere?

Nope, I just changed that autocratically when I implemented the global functions in Javascript instead of Java.

The thing is that loadModule() is different from the old importModule(), because it doesn't set a property in the calling scope, and includeModule() is different from the old importFromModule() because it only allows to include all properties, i.e. it is equivalent to importFromModule() called with "*" as second argument.

The reasoning behind these changes was that setting a property in the top level calling scope hampers importing a module locally (inside a function), and JS 1.8 destructuring assignment makes it possibible to selectively include properties from module:

  var {Foo, Bar} = loadModule('foomodule');

So the changed semantics made it easier for me to also change the names, but of course I should have written about it somewhere.

However, I was going to raise the issue again today, because I am again really unhappy with the loadModule()/includeModule() combo. Actually I think they both are fine meaning-wise, but they are just too long and verbose and cluttering for something used that often. It may have something to do with the camel case. I just feel it is too much to type in the shell, and it adds too much visual noise that makes it harder to see what is being imported.

So what I tried is dropping the *Module suffix, which gives us load() and include(). I think that's an improvement, but load() seems overly generic, so I'm leaning more towards using require() and include(), which accidentally is what Kris Kowal has been using all the time [1]. IMO require() makes for a really good keyword (and that's what we should look for, even if we don't use it as keyword in the strict sense of the word) because it is both concise and yet not too generic. It's true that it doesn't convey the fact that the function returns the module scope, but to me this is a lesser concern as it is immediately apperent when used in an assignment.

If you want to see how the code looks, or check out a working copy with the changed names, I created two git branches using load()/include() [2] and require()/include() [3].

[1] http://dev.helma.org/wiki/ModuleSystem/
[2] http://github.com/hns/helma-ng/tree/load_include
[3] http://github.com/hns/helma-ng/tree/require_include

I know there is a high cost in changing basics even at this early stage, mostly because it makes documentation, blog postings, existing code, and trained brain cells obsolete. I really hope this will be the last time we talk about changes in this area, but I think now is a good time to raise this topic one last time.

Hannes

 

Best regards,
-- Chris



Maksim Lin

unread,
Oct 2, 2008, 4:49:12 AM10/2/08
to helm...@googlegroups.com
Hi Hannes,

I really like the short require/include names, thoguh to be honest, my
old Java habits braincells were quite happy with
loadModule/includeModule too, but your right, in the nice world of
quick prototyping with the shell (which I find myself using more and
more),having short names does mkae life mcuh easier.

Also there seems to be a wierd bug with the windows version of git
1.5.3 complaining about 1 file (unittest.js) always being "not
uptodate" even after a fresh clone, though everything is fine when I
do it in linux. I'l check out the new 1.6 git for windows tommrow to
see if the problem is still there, but thought I'd give a warning to
anyone else struggling with git on win.

Maks.

Chris Zumbrunn

unread,
Oct 2, 2008, 7:04:34 AM10/2/08
to helm...@googlegroups.com
On Oct 2, 2008, at 10:40 , Hannes Wallnoefer wrote:

Hi Chris,

2008/10/2 Christian Langreiter <ch...@langreiter.com>

Dear Helmatics,

is the reasoning behind removing import[From]Module documented/
summarized somewhere?

Nope, I just changed that autocratically when I implemented the global functions in Javascript instead of Java.

[...]

I know there is a high cost in changing basics even at this early stage, mostly because it makes documentation, blog postings, existing code, and trained brain cells obsolete. I really hope this will be the last time we talk about changes in this area, but I think now is a good time to raise this topic one last time.

Since we talk about this one last time, and since it seems I never actually brought it up on the list so far, I'll just mention that my personal preference would still be to handle this using two objects, "Modules" for assignments and "module" for imports:

    module.helma.foo;

    var foo = Modules.helma.foo;

    var Bar = Modules.helma.foo.Bar;

So, for example, instead of...

    require('core.string');
    require('helma.webapp.request');
    require('helma.webapp.response');

    var continuation = require('helma.webapp.continuation');
    var system = require('helma.system');
    var server = require('helma.httpserver');
    var log = require('helma.logging').getLogger(__name__);

...we would get...

    module.core.string;
    module.helma.webapp.request;
    module.helma.webapp.response;

    var continuation = Modules.helma.webapp.continuation;
    var system = Modules.helma.system;
    var server = Modules.helma.httpserver;
    var log = Modules.helma.logging.getLogger(__name__);

I know you had reservations to that proposal, but I forgot what exactly those were. So, currently, this idea is still floating around in my mind as the potentially ideal solution. 

Cheers,
Chris

Robert Thurnher

unread,
Oct 2, 2008, 10:03:51 AM10/2/08
to helm...@googlegroups.com
2008/10/2 Hannes Wallnoefer <han...@gmail.com>:

> So what I tried is dropping the *Module suffix, which gives us load() and
> include(). I think that's an improvement, but load() seems overly generic,
> so I'm leaning more towards using require() and include(), which
> accidentally is what Kris Kowal has been using all the time [1]. IMO
> require() makes for a really good keyword (and that's what we should look
> for, even if we don't use it as keyword in the strict sense of the word)
> because it is both concise and yet not too generic. It's true that it
> doesn't convey the fact that the function returns the module scope, but to
> me this is a lesser concern as it is immediately apperent when used in an
> assignment.

Personally, I'm in favour of require() and include().
It's also the way Ruby does it. ;-)

-- Robert T.


>
> Hannes
>
>
> >
>

--
http://soup.robert42.com/

Christian Langreiter

unread,
Oct 2, 2008, 10:10:30 AM10/2/08
to helm...@googlegroups.com
> > is the reasoning behind removing import[From]Module documented/
> > summarized somewhere?
>
> Nope, I just changed that autocratically when I implemented the
> global functions in Javascript instead of Java.

Thanks for the extensive explanation!

My personal naming preferences are still oscillating wildly; on the
one hand, short names are generally good, on the other, I don't mind
the explicitness of the more verbose variants. For interactive work,
I'd rather introduce real shortcuts like l("foo") or r("bar").

In any case - in the name of DRY, I cast my vote to preserve an
importModule-like construct. IMO, even the verbose

importModule("helma.logging");

beats the noisy

var helma = {
logging: load("helma.logging")
};

any day (although I admit that, presumably, nobody would write new
code that way).

-- Chris

Chris Zumbrunn

unread,
Oct 2, 2008, 10:14:10 AM10/2/08
to helm...@googlegroups.com

On Oct 2, 2008, at 13:04 , Chris Zumbrunn wrote:

> module.core.string;
> module.helma.webapp.request;
> module.helma.webapp.response;
>
> var continuation = Modules.helma.webapp.continuation;
> var system = Modules.helma.system;
> var server = Modules.helma.httpserver;
> var log = Modules.helma.logging.getLogger(__name__);

The example above was ment for the case where the first three lines
would intend to import the modules into the current scope. Otherwise,
when loading these modules without importing anything from their
scope, the correct example would be:

Modules.core.string;
Modules.helma.webapp.request;
Modules.helma.webapp.response;


Chris

Hannes Wallnoefer

unread,
Oct 2, 2008, 11:27:17 AM10/2/08
to helm...@googlegroups.com
2008/10/2 Christian Langreiter <ch...@langreiter.com>

> > is the reasoning behind removing import[From]Module documented/
> > summarized somewhere?
>
> Nope, I just changed that autocratically when I implemented the
> global functions in Javascript instead of Java.

Thanks for the extensive explanation!

My personal naming preferences are still oscillating wildly; on the
one hand, short names are generally good, on the other, I don't mind
the explicitness of the more verbose variants. For interactive work,
I'd rather introduce real shortcuts like l("foo") or r("bar").

I don't think introducing single character function names would be a good idea. Don't forget that you have autocompletion in the shell. Explicitness and "non-generic-ness" is the reason I prefer "require" over "load", btw.
 

In any case - in the name of DRY, I cast my vote to preserve an
importModule-like construct. IMO, even the verbose

       importModule("helma.logging");

beats the noisy

       var helma = {
               logging: load("helma.logging")
       };

any day (although I admit that, presumably, nobody would write new
code that way).

I wrote that because there was a name clash with the logging function in that module. It's true that it is a bit awkward. I guess you'd just use a different variable name usually.

If I understand you correctly this is about setting up the namespace in the calling scope in the function, and not about naming, right?

I think we should look at the more common case:

    importModule("helma.logging");

vs.

   var logging = require("helma.logging");

The dont-repeat-yourself price goes to the first variant because the second one obviously repeats the word "logging". But on the other hand, the second variant is more explicit about what gets set, and it already handles the import-as case. And at least for my taste, with module names such as "helma.foo" I always chose to import as "foo" for reduced code verbosity.

Hannes
 

-- Chris



Christian Langreiter

unread,
Oct 2, 2008, 12:06:00 PM10/2/08
to helm...@googlegroups.com
> I don't think introducing single character function names would be a
> good idea. Don't forget that you have autocompletion in the shell.

Yeah, sure. That kind of invalidates the "shell convenience" argument
in favour of short names as well, tho ;-).

> If I understand you correctly this is about setting up the namespace
> in the calling scope in the function, and not about naming, right?

Yes.

> But on the other hand, the second variant is more explicit about
> what gets set, and it already handles the import-as case. And at
> least for my taste, with module names such as "helma.foo" I always
> chose to import as "foo" for reduced code verbosity.

All good points. Maybe it just takes time to grow on me (or I've been
brain-damaged too much from languages sporting dedicated import
syntax). On some level, the simple re-use of language features (e.g.
in JS-1.8-style selective import) is very cool; OTOH, there might be a
case to be made for making important structural attributes like import
relationships visually stand out (which, somehow, good ol'
importModule(...) does).

What about augmenting require with an optional second "target"
parameter? Too opaque?

var shell = require("helma.shell");

require("helma.shell", this);
helma.shell.exit(0);

-- Chris

Andreas Bolka

unread,
Oct 3, 2008, 9:34:40 AM10/3/08
to Helma NG
Excerpts from Hannes Wallnoefer's message of Thu Oct 02 10:40:44 +0200 2008:

> However, I was going to raise the issue again today, because I am again
> really unhappy with the loadModule()/includeModule() combo.

Summary: I suggest three functions require(), include(), and module()
with shortcuts r(), i() and m() provided in helma.shell.


Details:

I think there are three main use cases:

a) the old importModule() behaviour, I'll use require() for now
b) the current includeModule() behaviour, I'll use include() for now
c) everything else using the current loadModule(), I'll use module()

a) Python's import. require('foo.bar') sets foo.bar in the top level
calling scope. I.e. if foo.bar contains a function baz(), you'd call it
via:

require('foo.bar');
foo.bar.baz();

b) Python's from import *. include('foo.bar') sets a property in the top
level calling scope for each property in the module scope. So to call
aforementioned baz, you'd write:

include('foo.bar');
baz();

c) This includes importing only specific properties, import a property
under a different name, importing only locally inside a function, etc.
Some examples:

var bar = module('foo.bar'); // import foo.bar as bar

// with destructuring assignment
var {baz} = module('foo.bar'); // from foo.bar import baz
var {baz: qux} = module('foo.bar'); // from foo.bar import baz as qux

// w/o destructuring assignment
var baz = module('foo.bar').baz; // from foo.bar import baz
var qux = module('foo.bar').baz; // from foo.bar import baz as qux

This is extremely flexible and covers loads of use-cases well, except a)
and b). To get a)'s behaviour using you'd have to reconstruct the
namespace yourself:

var foo = {
bar: module('foo.bar')
}

which is quite ugly. For b) you'd have to enumerate all properties
defined manually. Cumbersome.

I therefore think, that providing three functions to cover those three
use-cases makes a lot of sense. I espcially think top-level qualified
imports as in use case a) are extremely useful and should be catered
for.


Which leads us to the second issue: how to name these beasts. For this
issue, I think it might be beneficial to discern usage in the shell and
in script files. For script files I personally do not care much how
these functions are called, as it's mostly a write once problem. In the
shell, on the other hand, a name can't be short enough :)

So maybe it makes sense to provide convenient shortcuts either
helma.shell (like r(), i(), m()) or in the shell directly (something
like \r or :r, etc.).


Once we've shortcut names and as long as the three use cases are catered
for, I don't really care what the actual names are. My current
preference would be the require()/include()/module() combo shown above.
But with shortcuts, more verbose things like
importModule()/includeModule()/loadModule() would be fine with me as
well.

Hannes Wallnoefer

unread,
Oct 4, 2008, 10:21:36 AM10/4/08
to Helma NG
On Oct 2, 6:06 pm, Christian Langreiter <ch...@langreiter.com> wrote:
>
> All good points. Maybe it just takes time to grow on me (or I've been  
> brain-damaged too much from languages sporting dedicated import  
> syntax). On some level, the simple re-use of language features (e.g.  
> in JS-1.8-style selective import) is very cool; OTOH, there might be a  
> case to be made for making important structural attributes like import  
> relationships visually stand out (which, somehow, good ol'  
> importModule(...) does).

Not sure what you mean - do you think they stand out because
importModule is such a heavy word, or because there is no var ... =
assignment in front of it?

> What about augmenting require with an optional second "target"  
> parameter? Too opaque?
>
>         var shell = require("helma.shell");
>
>         require("helma.shell", this);
>         helma.shell.exit(0);

I think this is a viable idea. IMO it is sound design principle that a
function should not modify an object unless the object is passed as an
argument - or the function is invoked as a method on the object. But
the latter wouldn't work for us in this case, as we can't tell the
difference between require() and this.require() from within the
require function. So yes, I do think this is a viable idea.

Hannes

> -- Chris

Hannes Wallnoefer

unread,
Oct 4, 2008, 4:13:52 PM10/4/08
to Helma NG
On Oct 3, 3:34 pm, Andreas Bolka <andreas.bo...@gmx.net> wrote:
> Excerpts from Hannes Wallnoefer's message of Thu Oct 02 10:40:44 +0200 2008:
>
> > However, I was going to raise the issue again today, because I am again
> > really unhappy with the loadModule()/includeModule() combo.
>
> Summary: I suggest three functions require(), include(), and module()
> with shortcuts r(), i() and m() provided in helma.shell.

You're the second person to request shortcuts in the shell, so I think
I should consider it. Isn't it nice that we have a helma.shell module
where we can put shell-only stuff, and it's easy to implement for
everybody? Maybe I'll try it out to get the feel of it.

> Details:
>
> I think there are three main use cases:
>
>     a) the old importModule() behaviour, I'll use require() for now
>     b) the current includeModule() behaviour, I'll use include() for now
>     c) everything else using the current loadModule(), I'll use module()

Well, to make it really complete I'd introduce another use case: the
core.* modules where you don't need any reference to the module, you
just want to make sure they are loaded. Of course I know that you can
just ignore the return value, but it is a separate case and to me the
only place where "require" really fits perfectly.

> a) Python's import. require('foo.bar') sets foo.bar in the top level
> calling scope. I.e. if foo.bar contains a function baz(), you'd call it
> via:
>
>     require('foo.bar');
>     foo.bar.baz();

One thing I was unhappy about in the original importModule() was that
it set up a property in the calling scope without being passed a
reference to it. This is possible because "this" defaults to the top
level scope. But I think I'd actually prefer Chris Langreiter's
proposal to pass the scope as second argument to the function. No
second argument -> the module scope is returned, second argument is
defined -> the module scope is defined in it using the module's name.

> b) Python's from import *. include('foo.bar') sets a property in the top
> level calling scope for each property in the module scope. So to call
> aforementioned baz, you'd write:
>
>     include('foo.bar');
>     baz();

Agreed.

> c) This includes importing only specific properties, import a property
> under a different name, importing only locally inside a function, etc.
> Some examples:
>
>     var bar = module('foo.bar');        // import foo.bar as bar
>
>     // with destructuring assignment
>     var {baz} = module('foo.bar');      // from foo.bar import baz
>     var {baz: qux} = module('foo.bar'); // from foo.bar import baz as qux
>
>     // w/o destructuring assignment
>     var baz = module('foo.bar').baz;    // from foo.bar import baz
>     var qux = module('foo.bar').baz;    // from foo.bar import baz as qux

I think module() is an interesting new naming option.

All things said, I think I'd prefer fewer functions with some
flexibility over having many functions with slightly different
purpose.

While writing this I just had a one hour chat with Andreas. One thing
is sure, we have a lot of options. Except import() of course. Although
we'd only have to comment out one line in Rhino to enable that, but
for some reasons I really don't want to do that.

Hannes

Kris Kowal

unread,
Oct 4, 2008, 11:04:11 PM10/4/08
to helm...@googlegroups.com
I think that shortcuts for import semantics should only be possible to
use in shell. For that reason, I favor the idea of using a
shell-command like ":r" or ".r", as has been previously suggested. I
fear that if they are available in normal scripts via your standard
library, people will abuse the brevity; I probably would have myself
when I was a younger programmer.

I'm, of course, glad that the general consensus appears to be moving
toward "require" and "include". I'm also planning, at this point, to
converge on Helma's "module.name" style module names instead of
"module/name.js" for client-side scripting. As long as we're all
moving toward this goal, I encourage anyone who is interested to read
Python's PEP on absolute and relative module names that is on track
for Python 3.

http://www.python.org/dev/peps/pep-0328/

I'm also glad that someone pointed out that just using "require" and
"include" left out the "import module.module" semantic, thus requiring
a third, "module", import function. Of course, I doubt I'm alone in
thinking that having three module import functions is worse than two,
just as you are not alone in thinking that three import semantics are
better than two (I mean, I agree: it would be good). Soo many
unfortunate tradeoffs!

Just to clarify the meanings of "require", "include", and "module", I
did notice that we implicitly shifted from the original convention:

import module.module
var module = {"module": require("module.module")}

from module.module import *
include("module.module")

from module.module import a, b, c
include("module.module", ["a", "b", "c"]); // transitional
var {a, b, c} = require("module.module");

To:

import module.module
require("module.module")

from module.module import *
include('module.module');

from module.module import a, b, c
var {a, b, c} = module("module.module");

This means that "require" became "module" and "require" became
something new to support the "import module.module" semantic more
cleanly. I personally don't ever use the "import module.module"
semantic, and, since I prefer to favor the Law of Demeter and I
usually reduce that pattern to "import module.module as module". That
is, I think "import module.module" is an outlier case that shouldn't
need to be supported since it's usually better to remap that pattern
to "import module.module as module", which, in the older convention,
is most easily expressed as "var module = require('module.module')".

That being said, I recognize that your code, coming from a Java
background, probably has a lot of cases of "import module.module" that
you would like to continue to support, if not encourage. To that end,
I think that we should reason about the ideal module system in terms
of two functions and provide a third, sub-optimally-named function to
support old code that you could immediately deprecate. For example,
"require" and "include" could retain their original semantics, and
"importModule" could remain for the transition, albeit re-implemented
in terms of "require".

Regarding the "require("helma.shell", this)" notation, wherein you
explicate the target of the assignment, I do concur that it's usually
better to expressly pass the modified object to a function. However,
this is a special case since the idea of a module loader is to add
"free variables": those that do not need to be referenced from an
underlying base object. That is, "require("helma.shell", foo)"
implies that you could access the imports thereafter as
"foo.helma.shell". However, "require("helma.shell", this)" implies
that you will be able to access "helma.shell" without any mention of
"this". Thus, the notation for importing modules, in my opinion,
should be orthogonal with the name of the object that carries those
free variables. Requiring an explicit target object might hinder
design flexibility. If I eventually convince this group to break
globals, locals, and the module object into separate entities, where
"this" would be the "module" object and "local" would be the implied
target for importing free variables into the module scope without
implicitly exporting them, code that does not explicate the target
object would be not need to change. Of course, there's no need for
you to tune your system to make it easy for me to potentially convince
you of additional features :-).

Next weekend, I'm driving about 600km north on the California coast to
meet with Ihab and the Google Caja team and to talk about modules for
ES3.1. Our dialectic on this list has genuinely helped me refine our
ideas of how future versions of JavaScript should support modules and
has given me a chance to discover the pros and cons of various
approaches from your perspective. Let's keep this discussion hot this
week so we can shape the future well.

Kris Kowal

Hannes Wallnoefer

unread,
Oct 13, 2008, 10:28:38 AM10/13/08
to Helma NG
Sorry for the delay, got hit by the flu again.

On Oct 5, 5:04 am, "Kris Kowal" <cowbertvon...@gmail.com> wrote:
>
> I'm also glad that someone pointed out that just using "require" and
> "include" left out the "import module.module" semantic, thus requiring
> a third, "module", import function.  Of course, I doubt I'm alone in
> thinking that having three module import functions is worse than two,
> just as you are not alone in thinking that three import semantics are
> better than two (I mean, I agree: it would be good).  Soo many
> unfortunate tradeoffs!
>
> Just to clarify the meanings of "require", "include", and "module", I
> did notice that we implicitly shifted from the original convention:
>
>     import module.module
>     var module = {"module": require("module.module")}
>
>     from module.module import *
>     include("module.module")
>
>     from module.module import a, b, c
>     include("module.module", ["a", "b", "c"]); // transitional
>     var {a, b, c} = require("module.module");
>
> To:
>
>     import module.module
>     require("module.module")
>
>     from module.module import *
>     include('module.module');
>
>     from module.module import a, b, c
>     var {a, b, c} = module("module.module");

Yes, I think that's what Andreas proposed. However, I still don't see
the necessity for a third function. I just don't think the "import and
define using module name" use case is so important, and I think the
following pattern isn't too ugly:

var namespace = {};
namespace.foo = require("namespace.foo");
namespace.bar = require("namespace.bar");

I think the most common case would be to bundle several modules into a
namespace object, in which case the overhead of the explicit namespace
definition becomes quite bearable.

> This means that "require" became "module" and "require" became
> something new to support the "import module.module" semantic more
> cleanly.  I personally don't ever use the "import module.module"
> semantic, and, since I prefer to favor the Law of Demeter and I
> usually reduce that pattern to "import module.module as module".  That
> is, I think "import module.module" is an outlier case that shouldn't
> need to be supported since it's usually better to remap that pattern
> to "import module.module as module", which, in the older convention,
> is most easily expressed as "var module = require('module.module')".
>
> That being said, I recognize that your code, coming from a Java
> background, probably has a lot of cases of "import module.module" that
> you would like to continue to support, if not encourage.  To that end,
> I think that we should reason about the ideal module system in terms
> of two functions and provide a third, sub-optimally-named function to
> support old code that you could immediately deprecate.  For example,
> "require" and "include" could retain their original semantics, and
> "importModule" could remain for the transition, albeit re-implemented
> in terms of "require".

Actually, I'm not using that pattern anywhere now. What I do a lot is
loading a module without setting a reference at all, e.g. for the
core.* modules which just add properties to the standard JS
prototypes. For these, the name "require" is a perfect fit IMO, and
it's an important point to be able to use require() with these without
setting up an unneeded reference.

> Regarding the "require("helma.shell", this)" notation, wherein you
> explicate the target of the assignment, I do concur that it's usually
> better to expressly pass the modified object to a function.  However,
> this is a special case since the idea of a module loader is to add
> "free variables": those that do not need to be referenced from an
> underlying base object.  That is, "require("helma.shell", foo)"
> implies that you could access the imports thereafter as
> "foo.helma.shell".  However, "require("helma.shell", this)" implies
> that you will be able to access "helma.shell" without any mention of
> "this".  Thus, the notation for importing modules, in my opinion,
> should be orthogonal with the name of the object that carries those
> free variables.  Requiring an explicit target object might hinder
> design flexibility. If I eventually convince this group to break
> globals, locals, and the module object into separate entities, where
> "this" would be the "module" object and "local" would be the implied
> target for importing free variables into the module scope without
> implicitly exporting them, code that does not explicate the target
> object would be not need to change.  Of course, there's no need for
> you to tune your system to make it easy for me to potentially convince
> you of additional features :-).

I understand. It's true the solution isn't great either. I think I
will just leave this and let everybody set their namespaces
explicitly, as outlined above.

> Next weekend, I'm driving about 600km north on the California coast to
> meet with Ihab and the Google Caja team and to talk about modules for
> ES3.1.  Our dialectic on this list has genuinely helped me refine our
> ideas of how future versions of JavaScript should support modules and
> has given me a chance to discover the pros and cons of various
> approaches from your perspective.  Let's keep this discussion hot this
> week so we can shape the future well.

Interesting. Let us know what came out of it!

Hannes

> Kris Kowal

Hannes Wallnoefer

unread,
Oct 28, 2008, 7:53:29 AM10/28/08
to Helma NG
A few things have happended since we last talked about the import
nomenclature.

First, I came to the conclusion that there is no good reason "import"
and "export" should be reserved words in Rhino. In fact these words
seem to be usable in all browsers except firefox, which uses them for
some legacy feature, and I see no good reason why that should affect
Rhino. I've filed a bug for this and committed the patch to Helma NG
git/svn:

https://bugzilla.mozilla.org/show_bug.cgi?id=461122

So it looks like we can use "import" and "export" after all.

Second, Andreas Bolka convinced me via jabber that the current
solution is aesthetically somewhat suboptimal:

require('core.string');
require('core.object');
var filters = require('helma.filters');
var log = require('helma.logging').getLogger(__name__);
var system = require('helma.system');
system.addHostObject(org.helma.template.MacroTag);

I agree that having import statements aligned and uniform is a
valuable goal. Therefore, I'm again considering the idea of
automatically setting the modules scope in the importing scope. With
"import" and "export", the result is suddenly really pleasing:

import('core.string');
import('core.object');
import('helma.filters');
import('helma.logging');
import('helma.system');

export('render', 'createSkin', 'Skin');

import() could check if the imported module export()s anything and
only set the module scope property in the calling scope if it does,
thus import('core.string') would not set up a core.string property in
the calling scope.

Andreas and me also talked about removing the "helma" prefix from core
modules such as system and logging, thus providing shorter import
names by default.

For a variant of import that does not set a property in the calling
scope, I would propose to use "require", and of course "include" would
remain what it currently is.

Something that is also on my mind is introducing a module() function
that allows to define a module and associate metadata with it:

module('foo.module', { version: 0.3, author: 'XY', url: 'http://xyz/
foo/' });

That could be used to include required version in import()/require()/
include():

import('foo.module', { requiredVersion: 0.3 });

We could also use the second argument to provide an alternative import
name:

import('foo.module', { as: 'foomod' });

What do you think?

Hannes

Hannes Wallnoefer

unread,
Oct 29, 2008, 6:43:00 AM10/29/08
to Helma NG
No opinions? I guess I'll just move ahead...

Hannes

Chris Zumbrunn

unread,
Oct 29, 2008, 7:46:17 AM10/29/08
to helm...@googlegroups.com

On Oct 29, 2008, at 11:43 , Hannes Wallnoefer wrote:
>
> No opinions? I guess I'll just move ahead...


The only thing I dislike is that the naming of "require" is now even
less appropriate, if I understand correctly what it will do. The
module's scope would be returned by require(), correct?

My current favorite alternative would be "bundle". Doesn't that
describe pretty well what it actually does?

var foo = bundle('helma.file');

Anyway, +1 in general, I really like the way import()/export() would
be used.

Chris

Chris Zumbrunn

unread,
Oct 29, 2008, 7:52:03 AM10/29/08
to Helma NG


On Oct 29, 12:46 pm, Chris Zumbrunn <chris.zumbr...@gmail.com> wrote:
> On Oct 29, 2008, at 11:43 , Hannes Wallnoefer wrote:
>
> > No opinions? I guess I'll just move ahead...
>
> The only thing I dislike is that the naming of "require" is now even  
> less appropriate, if I understand correctly what it will do. The  
> module's scope would be returned by require(), correct?
>
> My current favorite alternative would be "bundle". Doesn't that  
> describe pretty well what it actually does?
>
> var foo = bundle('helma.file');

...or

var foo = module('helma.file');

...and then using "define" or "defineModule" where you proposed to use
"module".

Chris

Christian Langreiter

unread,
Oct 29, 2008, 10:47:37 AM10/29/08
to helm...@googlegroups.com

On 29.10.2008, at 12:46, Chris Zumbrunn wrote:

> The only thing I dislike is that the naming of "require" is now even
> less appropriate, if I understand correctly what it will do.

I agree.

> My current favorite alternative would be "bundle". Doesn't that
> describe pretty well what it actually does?

In order of personal preference: load, module, bundle, require.

> Anyway, +1 in general, I really like the way import()/export() would
> be used.

+1! Looks much better than before.

-- Chris

Andreas Bolka

unread,
Oct 29, 2008, 8:59:47 PM10/29/08
to Helma NG
Excerpts from Hannes Wallnoefer's message of Tue Oct 28 12:53:29 +0100 2008:
> What do you think?

+1 from me (obviously :).

Regarding the naming of require: one place where this makes a lot of
sense is in using the modules that extend core prototypes, eg:

require('core.string');
require('core.array');

import('shell');
import('logging');

...

If you read require as "require this module to be loaded (and return
it)", it doesn't hold up _that_ badly in the other cases as well.
module(), my favourite personal alternative happens to do it the other
way round: module('core.string') for the former use-case looks strange,
wheres var foo = module('bar') looks great.

--
Regards,
Andreas

Hannes Wallnoefer

unread,
Oct 30, 2008, 7:29:18 AM10/30/08
to Helma NG
On Oct 30, 1:59 am, Andreas Bolka <andreas.bo...@gmx.net> wrote:
> Excerpts from Hannes Wallnoefer's message of Tue Oct 28 12:53:29 +0100 2008:
>
> > What do you think?
>
> +1 from me (obviously :).
>
> Regarding the naming of require: one place where this makes a lot of
> sense is in using the modules that extend core prototypes, eg:
>
>     require('core.string');
>     require('core.array');
>
>     import('shell');
>     import('logging');
>

My fanciest and newest idea was to couple the behaviour of import() to
the existence of an export() in the imported module. If the imported
module doesn't export anything, we can safely omit setting the module
scope property in the calling scope. So if the core.* modules didn't
export anything, import() could be used for them as well. I haven't
quite decided yet if this is an elegant solution or a confusing hack.
I think compared to having two kinds of import functions side by side
at the beginning of each module it would be an improvement.

Hannes

Hannes Wallnoefer

unread,
Oct 30, 2008, 7:47:11 AM10/30/08
to Helma NG
On Oct 30, 12:29 pm, Hannes Wallnoefer <hann...@gmail.com> wrote:
> On Oct 30, 1:59 am, Andreas Bolka <andreas.bo...@gmx.net> wrote:
>
> > Excerpts from Hannes Wallnoefer's message of Tue Oct 28 12:53:29 +0100 2008:
>
> > > What do you think?
>
> > +1 from me (obviously :).
>
> > Regarding the naming of require: one place where this makes a lot of
> > sense is in using the modules that extend core prototypes, eg:
>
> >     require('core.string');
> >     require('core.array');
>
> >     import('shell');
> >     import('logging');
>
> My fanciest and newest idea was to couple the behaviour of import() to
> the existence of an export() in the imported module. If the imported
> module doesn't export anything, we can safely omit setting the module
> scope property in the calling scope. So if the core.* modules didn't
> export anything, import() could be used for them as well. I haven't
> quite decided yet if this is an elegant solution or a confusing hack.
> I think compared to having two kinds of import functions side by side
> at the beginning of each module it would be an improvement.

Some more explanation on export():

My current plan is to have export() simply create an __export__
property containing the names of exported properties. To make it more
usable, it should be possible to call export() multiple times and pass
regular expressions to it:

export('foo', 'bar');
export(/moo.*/);

What do you think?

Hannes

Hannes Wallnoefer

unread,
Nov 6, 2008, 6:19:46 AM11/6/08
to Helma NG
An update to what I have now:

First, the idea to make the functionality of import() change depending
on whether the imported module export()s anything was absolutely
awful. It did help me add some missing export() calls to some helma
modules, but the behaviour of an imported module just not being there
is really confusing and hard to debug.

So I preserved require() for things like core.object and getting the
module scope as return value. Thus I now have a trinity of module
functions: include(), import() and require().

Regarding export(): unfortunately I still think we need to explicitly
export stuff to avoid include() to transitively include stuff the
imported module has imported. Not doing this will cause strange bugs
down the road IMO.

Regarding import-as functionality: I have added an optional second
argument to import() to define an local name different from the module
name. If we want to have "nice" import sections, that's just the way
to go. Thus, no renaming of helma.* modules so far. If you want to
import "helma.logging" as "logging", just do:

import('helma.logging', 'logging');

And about 'ugly', cluttered import sections: it's still possible to
write this, but I changed the style to separate import/require stuff
from actually setting up derived properties etc. So, in summary, what
previously looked like this in helma.skin:

require('core.string');
require('core.object');
var filters = require('helma.filters');
var log = require('helma.logging').getLogger(__name__);
var system = require('helma.system');
system.addHostObject(org.helma.template.MacroTag);

var __export__ = [
"render",
"createSkin",
"Skin"
];

... now looks like this:

require('core.string');
require('core.object');
import('helma.filters', 'filters');
import('helma.logging', 'logging');
import('helma.system', 'system');

export('render', 'createSkin', 'Skin');

var log = logging.getLogger(__name__);
system.addHostObject(org.helma.template.MacroTag);

I think it's better. What do you think?

This is still only in my local working copy, but I'm going to commit
it within the next hour or so.

Hannes

On Oct 30, 1:59 am, Andreas Bolka <andreas.bo...@gmx.net> wrote:

Andreas Bolka

unread,
Nov 6, 2008, 10:05:17 AM11/6/08
to Helma NG
Excerpts from Hannes Wallnoefer's message of Thu Nov 06 12:19:46 +0100 2008:

> ... now looks like this:
>
> require('core.string');
> require('core.object');
> import('helma.filters', 'filters');
> import('helma.logging', 'logging');
> import('helma.system', 'system');
>
> export('render', 'createSkin', 'Skin');
>
> var log = logging.getLogger(__name__);
> system.addHostObject(org.helma.template.MacroTag);
>
> I think it's better. What do you think?

Looks much better to me. +1!

--
Andreas

Christian Langreiter

unread,
Nov 6, 2008, 2:51:49 PM11/6/08
to helm...@googlegroups.com
> I think it's better. What do you think?

For sure!

Hannes Wallnoefer

unread,
Nov 6, 2008, 5:25:47 PM11/6/08
to Helma NG
On Nov 6, 8:51 pm, Christian Langreiter <ch...@langreiter.com> wrote:
> > I think it's better. What do you think?
>
> For sure!

Committed it.

Hannes
Reply all
Reply to author
Forward
0 new messages