Dear Helmatics,
is the reasoning behind removing import[From]Module documented/
summarized somewhere?
Best regards,
-- Chris
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.
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.
Personally, I'm in favour of require() and include().
It's also the way Ruby does it. ;-)
-- Robert T.
>
> Hannes
>
>
> >
>
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
> 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
> > 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
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
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.
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
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
> 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
+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
Looks much better to me. +1!
--
Andreas
For sure!