Namespaces as Sugar (was: complexity tax)

Brendan Eich brendan at
Tue May 27 13:07:48 PDT 2008

On May 27, 2008, at 11:42 AM, Maciej Stachowiak wrote:

> On May 27, 2008, at 11:00 AM, Brendan Eich wrote:
>> What's at issue is whether and why unqualified import matters in  
>> any object, even the global object only, since the NAS proposal  
>> did not allow unqualified import even at global level, and the use- 
>> case for unqualified import was dismissed as not compelling.
> There's really 4 separable issues:
> 1) Namespacing of names at global scope (via lexically scoped  
> reference).
> 2) Unqualified import of names into global scope.
> 3) Namespacing of arbitrary object properties.
> 4) Unqualified import of namespaces for arbitrary object properties.
> I would claim 1 and 2 are essential, 3 can be done by convention in  
> the absence of 4 (a la the NAS proposal) and 4 is unnecessary and  
> harmful to performance.

Thanks, this is helpful, since the argument you joined was about (2)  
and/or (4) -- there is no unqualified import in the NAS sketch.

I forgot one of the main use-case for 4, one you've expressed  
interest in already: 'use namespace intrinsic'. Without (4), this  
does not allow early binding for s.charAt(i) given var s:string to  
string.prototype.intrinsic::charAt. So you can't add one pragma and  
realize speedups or better type signatures for built-in methods. All  
you can do is access global intrinsic::foo bindings, which (in ES4,  
where opt-in versioning gets you immutable Object, Array, String,  
etc. without needing to open intrinsic) are few and not likely to  
realize a speed-up or more precise typing.

So early binding via namespacing would be gone. It could be  
reintroduced via an ad-hoc pragma. But that takes up complexity  
budget in the spec and real implementations too.

> It could save a lot of complexity, by not requiring any first-class  
> support for namespace lookup on arbitrary objects.

If I understand your proposal, meta::get or iterator::get could still  
be defined in an arbitrary object, and called with full qualification.

>> Lexical context, no dynamic open-namespaces scope.
> Note I said "compile to" so I think this was clear.

Just dotting an i, especially for everyone following along at home :-).

>> * internal namespace per compilation unit for information hiding  
>> -- hardcode as a special case?
> I'm not sure how this applies to unqualified import of namespaces  
> on arbitrary object properties.

Per the spec, internal is open in a separate set on the list of open  
namespaces. You don't have to qualify references to internal::foo.

>> * iterator and meta hooks in objects. Ugly __get__, etc., names  
>> instead?
> Unqualified import is not necessary for iterator or meta hooks.  
> Namespaces by convention (or __decoratedNames__) would be enough.

Agreed :-).

>> * helper_foo() and informative_bar() in the RI?
> I don't think any language feature should exist solely for the  
> convenience of the RI.

It's not just the RI. Namespacing for informative purposes in the RI  
is good programming style, akin to using helper namespaces in C++  
where top-level (but often class-level as you can see from reading RI  
builtins/*.es). `grep 'use namespace ' builtins/*.es` runs to 41 lines.

> If more effort must be spent the necessary complexity of the  
> language just to preserve current levels of performance,

No, to increase the levels of performance for the current version of  
the language. That's the horse going over the first hill from the barn.

Adding namespaces to the next major version could slip into the  
optimization frameworks under way in a couple of engines I know of.  
But you may doubt. That's ok. I maintain that the high order bit  
should not be the cost to implementors, rather utility vs. complexity  
facing users.

> then that takes away resources from implementing unnecessary  
> complexity to improve performance beyond current levels.

Or harmonizes with the "unnecessary" complexity that implementors  
trying to compete already face. Let's not prejudge the ability of  
competing implementations to (a) speed up; (b) optimize namespace  
search for common code. The first target has to be the programmer  
using the language, not the implementor. This does not mean  
implementor concerns do not matter, as I keep trying to reassure you  
-- only that they come second.

> In general, keeping the language simpler is good for performance.

Yet focusing on performance can complicate the language for  
programmers. It certainly has for other languages.

> Other things being equal, simpler implementations are easier to  
> change, and have more room to add complexity for optimization  
> without becoming too complex for human understanding.
> I will agree that some added language features are essential but I  
> think minor improvements in expressiveness that have large  
> complexity cost are a poor tradeoff.

Hard to disagree without more details. This is motherhood and apple  
pie stuff.

>> This is a good trade-off if it can be done in reasonable  
>> footprint, since there is a huge installed base, and a pretty-big  
>> knowledge base. We're not, for example, going to remove the  
>> ability to replace String.prototype.charAt, in any compatible ESn  
>> version.
> Preserving compatibility with existing performance-harming features  
> is not the same thing as introducing new ones.

Since I did not say it was, I'm not sure why you are writing this.  
Obviously the bone of contention is benefit to programmers vs. cost  
to implementors. You allow as how the language must grow, which is a  
challenge to implementors, if not to programmers (who should see  
reduced need for boilerplate and library solutions). So we should get  
back to details.

> Programmers have found ways to live with today's performance, and  
> it is true that for some applications core language performance is  
> not the bottleneck. But that doesn't mean we can feel free to  
> ignore performance considerations in the design of new language  
> features.

Yeah, yeah.

I do not mean to blow off this point. We've discussed performance and  
footprint throughout the last two+ years. No one feels free to ignore  
performance considerations, and what I wrote did not ignore those  

>>> I suspect the answer to this in AS3 is that if you want  
>>> performance, you have to use type declarations.
>> That may be the case for AS3 code using Flex, but even such a  
>> result would be informative -- don't open multiple namespaces if  
>> you're using untyped objects and targeting an unoptimized  
>> implementation.
>> But really, why is any of the several feature combinations that  
>> could hurt performance (with, eval, deep scope chains and  
>> prototype chains in most implementations) a reason to cripple the  
>> language? Performance is not king, and JS ain't C++.
> Is removing unqualified import of namespaces at non-global scope  
> (the only aspect of namespaces that seems prima facie harmful to  
> performance) really "crippling the language"?

Depends on how much you value 'use namespace intrinsic' in  
particular, and the ability open "expert" or "version2" namespaces  
that cut across programs, classes, and objects in ES4 applied to  
large-scale programming in general.

I want to say thanks for making this proposal (open namespace search  
only for lexical references). It leaves most of the use-cases I cited  
intact. Well done, good compromise (not complete evisceration of  
property qualifiers, or dismissal of unqualified import).

The helper/informative usage in the RI does open those namespaces,  
and intrinsic works only as a cross-cutting namespace that can be  
opened via pragma. So we should focus on these, if only to agree to  
disagree on the importance of the former, argue for hardcoding the  
latter via a separate early-binding pragma, and so forth.


More information about the Es4-discuss mailing list