shortcuts for defining block-local private names, plays nicely with @foo syntax

Herby Vojčík herby at mailbox.sk
Mon Jan 23 13:05:17 PST 2012


Brendan Eich wrote:
>> Herby Vojčík <mailto:herby at mailbox.sk>
>> January 23, 2012 11:51 AM
>> Brendan Eich-2 wrote:
>>>> Herby Vojčík<mailto:herby at mailbox.sk>
>>>> January 23, 2012 3:21 AM
>>>> module {
>>>> private foo, bar;
>>>>
>>>> export function factory1 (args) { ... }
>>>>
>>>> export function factory2 (args) { ... }
>>>>
>>>> but then foo and bar are known to the rest of the module. It was not
>>>> the premise.
>>> There is nothing wrong with this solution, and everything right. If you
>>> want to keep foo and bar secret from other modules, you've done so. If
>>> you, for some reason, want to keep them secret from other contents of
>>> this module, use a block (but you'll have a harder time exporting --
>>> export must come at module top level).
>>
>> In other way, it is impossible to have truly lexical-local private names.
>
> No, the private binding form that is desugared from
>
> private foo;
>
> to
>
> const foo = Name.create("foo");
>
> or better (with hygiene to use the right Name, etc.) is lexical in its
> binding structure.

Ok, let me be as exact as possible. I've got caught on imperfect 
formulation often.
It is impossible to have lexically-local 
runtime-shared-over-the-subsequent-invocations private name.

>> You must define them all at the upper level, tens or maybe hundreds of
>> lines
>> back.
>
> What?
>
> We're talking about singletons -- you seemed to agree. Why should deeply
> nested declarations be evaluated only once (at compile time? What does
> that mean exactly?

That means exactly: "Here I want to use foo as a private identifier, 
known only to me." And of course, since it is a (private) identifier, it 
should be identical in all invocations, in other words, shared between 
invocations.

> that mean exactly? Or somehow first-run-only, at their nesting depth...)?

Since all I want to do is to "have a private identifier to use", I do 
not actually _run_ anything, in a typical sense. If you look at it nogt 
through imperative desugaring (forget it for a while), but as an 
aliasing (of a singleton hoisted up automatically), there is no need to 
be afraid of "Why should deeply nested declarations be evaluated only 
once" or "somehow first-run-only". I am not talking about generic 
mechanism of static a la C expressions stored in singletons.

I am only talking about pre-generated private names (deeply-frozen 
immutable objects) and aliasing them deep down.

They are like identifiers (private names really have this nature, when 
paired with @). Or they are like string constants. They are also created 
once in compile-time (or it seems so, implementation details aside) and 
nobody seems to be bothered.

> There's no difference here between other forms, including regular
> expression literals and function expressions and declarations. A
> function declaration in particular is evaluated ot a fresh object bound
> to the declared name. The desugaring is complicated by hoisting, but
> consider a magic hoist primitive:
>
> function bar(params) {body}
>
> desugars to
>
> hoist bar = function bar(params) {body};
>
> and you get a fresh object per evaluation.

True. You probably want to bring an analogy that function declared 
within block is always different in each invocation, that is, hoisting 
is only inside innermost function/module/program scope...

> You want to mislocate private for some reason. Why? Are you focused too
> much on class constructors?

... but as I already hinted, I see a difference in purpose between 
things like function and the private name. That is why I want to 
"mislocate" it.

Function is "living" object, having input, processing and output. I see 
private name as a "dead" thing, akin to identifier. The identifier 
defined deep down is lexically-local, but it is always the same 
identifier in every invocation.

I can show an analogy on this: You access foo.bar, and you access 
foo. at baz in the same block. In all invocations, foo.bar is always 
foo["bar"], "bar" is always the same constant (foo may be different). It 
is very probable, that you have the same purpose there for foo. at baz, the 
only difference being public/private, that is, so that foo. at baz is 
always foo[_the_shared_baz_private_name].

I have written in my first post, that there definitely are other uses of 
private names, where you want them used more dynamically (there are also 
many uses of public name, where you want then used more dynamically, and 
you use foo[dynamic_expression] for that). But there are more of plain 
foo.bar there. That is why I say there is more of 
foo. at the_shared_lexically_local_bar.

You need not to declare identifiers. You simply write them and you have 
them. But it is not do for private names playing the identifier role. 
For them, the 'private foo' as a means of declaration 'foo will be used 
here as a private name playing role of identifier, that is, being the 
same in all invocations' was devised.

(and yes, classes are typical use case. But it is not special for 
classes in 'class keyword' but in broader term - anywhere when things 
with similar structure are cooperating)

> The module with two functions sharing private names use-case is
> addressed by module-scope private declarations. What is the syntax you
Not if they are deep down, included in, for example, class and inside 
that, included as local functions inside a method (for example). There, 
the name local only to the innermost lexical scope would be much better. 
For the locality is good thing. You can easily move such code (with 
locally defined private names) anywhere and it will simply work.

By saying: if you want shared private identifier, you must create it at 
runtime, so you must define it at the (module|program), you are breaking 
locality and forcing relation of things that are apart. Not nice, will 
lead to bugs.
> want there?
>
> /be

I hope at least now I was understood better.

Herby


More information about the es-discuss mailing list