Addition of a global namespace function?

Mike Samuel mikesamuel at gmail.com
Thu Dec 3 18:59:24 PST 2009


2009/12/3 Mark A. Ziesemer <online at mark.ziesemer.com>:
> On Thu, Dec 3, 2009 at 12:37 PM, Mike Samuel <mikesamuel at gmail.com> wrote:
>
>> Thanks for responding in such detail.
>> I have responded inline, but to summarize what I think I've understood so far:
>> (1) This is a proposal for an added function, without any proposed
>> syntactic sugar
>
> Agreed, it is a proposal for an added function.  Not sure about the
> proposed syntactic sugar.
>
>> (2) The function can be implemented as a library function as in your blog post
>
> Agreed, though this would only be for compatibility until implemented
> natively.  A similar example of this is
> https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/forEach#Compatibility
> .
>
>> (3) The goal is to reduce the chance of unintentional namespace
>> collisions by code that is aware of the namespace function.
>
> Agreed, though not only between code that is aware of the namespace
> function, but also allowing code that is aware of the namespace
> function to better package itself (use no more than 1 global
> variable), to reduce the chance of collisions with code that is not
> aware of the namespace function.

Quite.  But the collisions are reduced only one way with legacy code.
If code is not aware of the namespace function, then it can still
clobber code that is.


>> (4) It is not a goal to prevent namespace collisions between code
>> written before the namespace function and code written after.
>> Specifically, if legacy code did "com = 'foo'" then namespacing would
>> be broken for all subsequent code.
>
> Somewhat.  Any children of "com" would no longer be accessible by
> "com".  However, any code within the closure(s) that was a child of
> com could possibly continue working properly, but just not able to be
> externally referenced.  Furthermore, if something did run that
> reassigned "com", as long as that other code was just using it
> temporarily, the reference could be "backed up and restored".  For
> example:

Ok, so code that is aware of legacy code that clobbers a namespace can
workaround the fact as long as both don't depend on the namespace
surviving after they've run.

>
> namespace("com.example.something");
> // com.example.something is further populated.
>
> // Legacy JS now needs to be included, which uses "com" as a temporary
> String variable.
> // We are aware of this, and will handle accordingly.
>
> // Backup:
> var comBackup = com;
>
> // Allow legacy code to run:
> legacyCode();
> // com now is "1234", but will actually never be referenced by the
> legacy code again.  Restore:
> com = comBackup;
> delete comBackup;
>
> // com.example.* can now be referenced as intended.
>
> This is already an issue and a practice with namespace variables, such
> as "$" used in jQuery.  See
> http://docs.jquery.com/Using_jQuery_with_Other_Libraries .

An issue that namespace() does not address.

>> So is it your argument that both (prefixing and dotted path
>> namespacing) are conventions that address the same problem, but that
>> the java-esque package name convention is a better convention?
>
> Somewhat.  Both conventions help to eliminate naming collisions.

eliminate -> mitigate?

> However, the "dotted path" namespacing respects the global namespace,
> where as every namespace created using prefixing is another entry in
> the global namespace.

Now you get at most one global entry per TLD.


> There have been some concerns that using the nested objects may affect
> performance, due to having to resolve multiple entities.  However,
> I've not seen any results of any tests that show this to be true, or
> at least not with any significant additional expense.  This is further

Here is a console session from the squarefree shell on FF3.5

(function () { var abs = Math.abs; var t0 = Date.now(); for (var i =
100000; --i >= 0;) { abs(-1); } var t1 = Date.now(); return t1 - t0;
})()
9
var js = {}; js.lang = {}, js.lang.Math = { abs: Math.abs }
[object Object]
(function () { var t0 = Date.now(); for (var i = 100000; --i >= 0;) {
js.lang.Math.abs(-1); } var t1 = Date.now(); return t1 - t0; })()
65

Using a 4 deep path vs a local variable results in about a 7x slowdown.


> mitigated through "aliasing" as previously demonstrated.  I think the

True.  This aliasing will of course defeat a lot of the dynamism, such
as using getters to do lazy loading.
I suppose minifiers and optimizers could introduce aliases at the top
of a closure to do some optimization, but only if the namespace name
is statically determinable.  Essentially what is described at
http://www.daveoncode.com/2009/12/01/custom-javascript-classes-packages-using-goog-provide-and-goog-require/

> same performance concerns could be valid with prefixing, as the large
> number of objects created at the global level can also hinder
> performance, as hash functions used in the implementation for the
> lookups may become less-than-optimal for a large number of items.
> Even if it doesn't hinder the performance of the runtime, consider
> tools such as the DOM inspector in Firebug.  The window works much
> better with 10,000 objects stored in a tree structure with only 20 or
> so objects at the top level, rather than having all 10,000 objects
> displayed at once.
>
>> Have you quantified the cost of each module carrying their own
>> namespace creating code?
>> The bit in your blog about the minified form of the namespace function
>> suggest that it is low.
>
> The issue isn't so much about the amount of code.  One issue is that

Code size is an issue for many people.

> each module that uses a namespace function also needs to make sure
> that such a function is properly named or namespaced as to not
> conflict with a similar namespace function in other modules.  It is
> also difficult to encourage the use of namespacing when a proper
> method to utilize namespacing is not readily available.  I don't agree
> that each module should have to include this additional boilerplate
> code.

They will until/if 90+% of browsers support your namespace function.

>> And if the function is in the minimized code, then can't minifiers do
>> a better job since they can do whole program analysis to use an
>> identifier smaller than the 9 character namespace, whereas if
>> "namespace" where externally defined, the 9 character name could not
>> be shortened.
>
> I'm somewhat confused.  While the implementation of "namespace" may be
> minified, the reference to the "namespace" function itself should
> never be minified.  This is part of my proposal - it should be made
> globally available.  Also, if/once made part of the language standard,
> the will be no need to include the implementation of the "namespace"
> function, other than for backwards-compatibility, as mentioned above.

Yes, but if they carry the code themselves then a code optimizer can convert
  function namespace(qname) { ... }
  namespace('foo.bar') = ...
  // many more
to
  function a(qname) { ... }
  a('foo.bar') = ...
  // many more
saving space when 7 * nCalls > namespace.toString().length

I suppose if the namespace function is a const, they could provide a
local short name by aliasing, so please ignore.


>> Since we are talking about language extensions, we need not be limited
>> by the approaches that are easy in ES3.
>> We can consider approaches that use other language primitives (like
>> lexical scopes in Ihab's module proposal) to address the same goals as
>> namespace.
>
> Does this even need to be considered a language extension?  Nothing

ES-harmony is talking about language extension.

> I'm proposing is changing the language specification - just making an
> additional function available globally and by default.  This will also
> allow it to be easily "back-ported" (as with the Array extras, etc.),
> allowing the function to be used to aid against an immediate and
> growing issue (naming collisions, etc.)



>> Let me make sure I understand.
>> Let's say you have a file foo.js that inroduces one API element
>> com.ziesemer.foo.  There are two namespaces:
>>  com
>>  com.ziesemer
>> as a result of
>>  namespace('com.ziesemer').foo = ...;
>>
>> com.ziesemer.foo is not a namespace, since it is not created as a
>> result of the namespace function, right?
>
> I'm not necessarily encouraging this, but "foo" could be a function,
> but could also act as another namespace level.  For example, all of
> the following could be valid:
>
> com.ziesemer.foo("bar!");
> com.ziesemer.foo.child();
> com.ziesemer.foo.a.b();
>
>> So for namespaces to be dynamic, means it would be OK for com.ziesemer
>> to be implemented in terms of a getter -- e.g. to allow lazy loading?
>
> I have to think more on this.  Need to review the concept of "getters"
> in the language.

Section 8.12 of the spec defines the semantics.  A good starting point
might be 15.2.3.6 Object.defineProperty.

>> > any reason to introduce artificial limitations to prevent doing so.
>>
>> There are more clients for javascript programmers than just
>> programmers and interpreters.  There are debugging tools, minifiers,
>> IDE auto-completers, documentation generators, etc.
>
> I don't see anything that conflicts with this.  This practice is
> already being used increasingly, and I've not seen any related issues
> - including with debugging tools e.g. Firebug, or minifiers such as
> the YUI Compressor.

>> >> Why is namespace not in a namespace?
>
>> Lots of systems require bootstrapping.  In java, there is no paradox
>> when it comes to which ClassLoader loaded the Class or ClassLoader
>> classes?
>
> True.  The same could be done as:
>
> var com = {};
> com.example = {};
> com.example.namespace = function(){ /* ... */ };
>
> com.example.namespace("com.example.test");
>
> The better argument against this is probably that then every library,
> etc., that wants to namespace will have to call
> "com.example.namespace(...)" instead of just "namespace(...)".  While
> not impossible, it seems silly to bury what should be such a
> fundamental function.

Perhaps I just don't understand why it is so fundamental.
It seems that if OO namespacing becomes the norm, most code modules
will look like
(function () {
   // aliases to externs
   var foo = namespace('com.foo');  // or an imports object composed
from import...as decls

   // core code

   // export definitions -- 1 use of namespace
   mixin(namespace('com.bar'), { export1: ..., export2: ... })
})()
So it seems that it wouldn't really be fundamental in core code --
just idiomatic for import and export.


>> I appreciate that you're trying to codify existing practice.  I'm
>> trying to help clarify the proposal by asking these questions.  Lot's
>> of existing practices (JSON handling and getElementsByClassName come
>> to mind) have many subtle variants, so codifying is not a simple
>> matter of stamping approval on a concensus -- you still have to tease
>> out goals, enumerate candidates, discuss tradeoffs, understand legacy
>> code, etc.
>
> Perfectly agreed and appreciated.
>
>> This seems like a non-sequitur.
>>
>> If two pieces of code do
>>    namespace('com.ziesemer') = ...
>>
>>    namespace('com.jquery') = ...
>> isn't the com object a shared concern?  Why is it "just an object"?
>
> I guess I don't see the issue.  Following the namespacing practice,
> one shouldn't define anything that they don't "own".  I could take
> ownership of "com.ziesemer" and assign things to it, or re-assign it.
> However, I don't own "com", so I shouldn't touch it.  It is still just
> an object, just not one that anyone should pretend to own.

But you do touch it.  You add a 'ziesemer' property.
And if "ownership" is the right concept, on Rhino, it is owned by the
local environment.

>> Wouldn't it be problematic for code to do something like (using the .info TLD):
>>  var info = loadUserInfo();
>>
>>  namespace('info.ziesemer').foo = ...;
>
> Probably.  However, using "info" as a top level variable was the first issue.

Yep, just an example of another way for this to interact badly with legacy code.
There are many other TLDs that might be names in the top level or IDs
exported since window includes document.all on IE -- travel, museum,
biz, int, us, etc.

> Additionally, the goal for this proposal isn't necessarily to define
> the namespacing standard - just the function that can be used to
> create namespaces.  Another standard - either along with or after this
> one - may clarify this.  Also, if everyone just standardized on this,
> there would be no further work to do.  (High hopes, I realize, but not
> impossible, or anything that I think should delay this proposal.)

I don't know quite what a "namespacing" standard would look like, but
if your goal is to get large numbers of developers to do things "the
right way", a standard is not the right way to go.  Standards can only
help a small group of developers coordinate, e.g. browser
implementors.

I'm not yet convinced that namespacing is the problem to solve  -- it
sounds like solving the module problem would moot namespacing.


>> If the first module creates an object, then the second will not modify
>> it.  But the second is problematic in this case precisely because it
>> assumes things about info that are not true.  If it is just an object,
>> instead of "an object that other packages can safely add properties to
>> without changing the meaning of namespace respecting programs" then
>> namespacing is broken.
>
> See above.  I don't see any issues with both working together.
>
>> (I think in Rhino it's not just an object.  If there's any com package
>> on the classpath, it's actually an immutable java package item that
>> allows Rhino to get at java classes.)
>
> Very true.  However, again, this isn't an issue with the proposed
> namespace function itself, but the standard for how namespaces are
> created.

True.  It's hard to reason about how the namespace function should
behave without considering one or more naming schemes.


> IMO, the JavaPackage's of "com", etc., should be eliminated and
> instead referenced through the existing "Packages" JavaPackage.  I.E.:
> "Packages.com.sun" instead of "com.sun".

I don't understand Rhino internals well, but I think the two have
subtly different meanings.
I agree though.  But Rhino and liveconnect do raise legacy concerns
for java-esque naming schemes.

>> > I would say that namespaces should definitely not be marked
>> > "unconfigurable".  A good JavaScript library, if it has nothing else
>> > to do and no more purpose, should actually remove itself from memory,
>> > and allow itself to be garbage collected.  I don't see any reason to
>> > introduce any barriers to allowing this.
>>
>> How would this be done in practice?
>> How would a library know when to unload itself?
>
> I'd consider this to be out-of-scope of this discussion.  However,
> it's probably more of an issue of not "how this would be done", but
> "it's probably the wrong approach, anyway".

Ok, so supporting unloading of code modules is definitely not a goal
of the namespace function.

> Such code should just be handled in a closure, so the entire thing can
> go out-of-scope and be GC'd when complete:
>
> (function(){
>  // Stuff...
> })();

But dependencies it loads and accesses via namespace(...) could not do that.
Code that does not use namespace(...) to export don't require manual
deallocation, but their dependencies do.

> However, for the point of this discussion:
>
> namespace("com.example.myApp");
> namespace("com.example.helperA");
> namespace("com.example.helperB");
>
> "myApp" needs to utilize "helperA" and "helperB", but only
> temporarily.  Additionally, during this time, "helperA" and "helperB"
> may call each other, which is why they need to have a namespaced
> reference defined outside of a closure.  After this, "myApp" could
> remain, with the "helpers" removed:

Only if myApp knows it "owns" helperA and helperB in the sense you used above.

> com.example.myApp.cleanupHelpers();
>
> Where cleanupHelpers was previously defined as:
>
> com.example.myApp.cleanupHelpers = function{
>  delete com.example.helperA;
>  delete com.example.helperB;
> };
>
> More appropriately, both helpers could define their own cleanup()
> functions, that could delete themselves in the same way.

Personally, I dislike imposing manual memory management on languages
that have a garbage collector.
But you're right that this is not a problem with the namespace
function, just with code loading schemes that depend on the global
scope.


>> If another approach to managing namespace collisions were to do this
>> better, would it be an argument against the OO namespacing approach?
>
> I wouldn't want to do anything to hinder further progress or
> enhancements around this.  However, I don't think anything new would
> be an argument against what is being proposed.  As mentioned
> previously, many libraries (especially the better ones) are already
> utilizing the OO namespacing approach.  Right now, each library is
> creating and using their own "namespace" function.  Scripting uses
> that currently aren't using a library must either consider using a
> library to obtain a namespace function, or define one themselves - at
> the risk of a naming collision for the namespace function itself, etc.

Ok, but hermetic eval based loaders would allow those libraries to
work and take advantage of any namespace function without losing the
ability to choose your own name, and without adding anything to the
global scope which makes collection difficult.

> I'm hoping that a provided namespace function can become standardized
> into the language, and made available at runtime to previous versions
> as demonstrated above.

>> Do we agree that the ability to use `with` with OO namespaces is not a
>> feature of the proposal.
>
> Agreed, not a highlighted feature.  It is something that probably
> should still be listed on a FAQ page, etc., that helps explain the
> usage and design, but also the previously mentioned links that show
> why it is not the best idea and why the other methods of aliasing
> should be used instead.
>
>> Java packages provide for many things.  I am trying to understand via
>> your analogy which you see this as providing.  As far as I can tell,
>> java packages provide:
>>  (1) access control via package private and JAR sealing
>>  (2) hierarchical addressing of classes
>>  (3) some degree of namespace separation, though separation can also
>> be achieved by using multiple classloaders
>
> Even JAR sealing doesn't provide access control.  Classes and methods
> within a sealed package can still be called.  Sealing just means that
> additional classes in a sealed package can't be defined outside of the
> same JAR.

> I don't think this is really applicable to this proposal, though.

Agreed. Not applicable.

>> > I guess I would consider the "private" and "protected" keywords more
>> > suited to "hiding" things than packages.  Even then, these keywords
>> > are seldom misunderstood, and should never be used for "security".  A
>> > little reflection can go a long way!
>>
>> I disagree with this.  Reflection does not work around the access
>> control rules unless the VM invoker explicitly opts out.  See Joe-E as
>> an example of security based on access running a subset of Java with
>> reflection enabled.  http://code.google.com/p/joe-e/
>
> True - as long as these details are understood.  I was just saying
> that I see the "private" keyword being used way too often as a failed
> "security measure".



> --
> Mark A. Ziesemer
> www.ziesemer.com
>


More information about the es-discuss mailing list