Namespaces as Sugar (was: complexity tax)
Brendan Eich
brendan at mozilla.org
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
considerations.
>>> 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.
/be
More information about the Es4-discuss
mailing list