Addition of a global namespace function?

Alex Russell alex at dojotoolkit.org
Fri Dec 4 08:41:20 PST 2009


CommonJS modules don't solve the global pollution problem, because they can't. We're gonna keep blowing off limbs until we acknowledge that there's a design flaw in the language and take some positive action at a semantic level to correct it.

Regards

On Dec 4, 2009, at 5:20 AM, Irakli Gozalishvili wrote:

> Hi Mark,
> 
> After looking at your posts, I do have a feeling that what you actually
> trying to solve is not a namespacing but modules scope encapsulation.
> Besides they way you're proposing to solve this is not really suits well for
> the interpreted language. I would recommend you to look into commonjs, (
> http://commonjs.org/, http://wiki.commonjs.org/wiki/Modules/) I think it
> solves the problem nicely without extending language itself. One more
> interesting to look at http://www.cmlenz.net/archives/2009/12/namespaces
> 
> --
> Irakli Gozalishvili
> Web: http://rfobic.wordpress.com/
> Phone: +31 614 205275
> Address: Taksteeg 3 - 4, 1012PB Amsterdam, Netherlands
> 
> 
> On Fri, Dec 4, 2009 at 03:59, Mike Samuel <mikesamuel at gmail.com> wrote:
> 
>> 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
>>> 
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>> 
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss

--
Alex Russell
slightlyoff at google.com
alex at dojotoolkit.org BE03 E88D EABB 2116 CC49 8259 CF78 E242 59C3 9723



More information about the es-discuss mailing list