Design principles for extending ES object abstractions

David Herman dherman at mozilla.com
Tue Jul 12 11:28:26 PDT 2011


> My understanding of generators was naively that they are syntactic sugar for defining an iterator.

Well, I think I understand what you're getting at: there's a sense in which generators don't add the ability to do something that's *absolutely impossible* to express in ES5.

OTOH, generators make it possible to express something that otherwise -- in general -- requires a deep translation (in particular, a CPS transformation of the entire function). This is more than syntactic sugar. This is a new expressiveness that wasn't there before. The slope between Felleisen-expressiveness ("is it possible to do X without a deep transformation?") and Turing-expressiveness ("is it possible to do X at all?") is a slippery one.

I don't want to quibble about philosophical pedantry, but that's really all I mean by my shyness about General Principles. They're strictly *harder* to decide on than the problem at hand. It's too easy to lose focus and waste time arguing abstractions. And then even when we agree on general principles, when we get into concrete situations it's too easy to start quibbling about exactly how to interpret and apply the principles for the problem at hand.

I prefer to work out from the middle of the maze, learning and refining principles as I go, rather than trying to decide on them all up front.

> Re-reading the generators proposal, I was concerned at first that somehow the semantics of the syntactic desugaring might be taking dependencies on the internal properties of the generator objects when consumed in a generator, such as in a “yield* other”.  However, it looks like even there, the semantics are in terms of the public API on the object, so that a user defined object that provides next/send/throw/close can correctly interoperate. 

Yup, nothing to worry about there. Fear not the yield*.

> I haven’t yet been able to intuit from the module_loaders page what is needed to accomplish each of the above though.  For example, if it is the case that loading the “@name” module required putting all my code in a callback passed to SystemLoader.load, that feels like it might be too heavy.  Do you have examples of what each of these would look like given the current proposal?

I need to have another go 'round at the module loaders API, and I will get there before too long. Sorry about that.

But it should not be necessary to have a callbacky version for builtin modules like "@name" -- the API should include a simple, synchronous way to test for the presence of modules in a loader. So it should be possible to do something like SystemLoader.getLoaded("@name").

> (1) why is a child loader needed?

Not needed, just the point of the example. Let me make the example more concrete:

You're writing an online code editor like CodeMirror or ACE, and you want to support the programmer running JS code from within the editor. So you want to run that code in a sandbox, so that it doesn't see the data structures that are implementing the IDE itself. So you create a child loader. Now, within that child loader, you might want the ability to construct some initial modules that make up the initial global environment that the user's code is running in. So you use buildModule to dynamically construct those modules.

> (2) any particular reason why the buildModule and registerModule are separated?

Because you might want to build a single shared module instance that you register in multiple loaders. These are orthogonal primitives that can be composed. It may also make sense to have conveniences for common operations, layered on top of the primitives.

> (3) Would this allow declaring module dependencies for the new module?  As one comparison, the require.js module definition syntax is simpler in terms of APIs, but also requires an extra closure due to module dependencies, which may also be needed in the model above:
>  
>     define("m", [], function() {
>         return {
>             x: 42,
>             f: function() { …  }
>         }
>     });

I think the more straightforward approach is just to pre-load (and pre-register in the loader, if appropriate) whatever dependencies are needed.

> ASIDE: It still feels a bit odd to see ES5 syntax running on ES.next runtime referred to as ‘legacy’.

No pejorative overtones intended. We just don't yet have any decent terminology for distinguishing the full ES.next front end from the backwards-compatible one.

Dave

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20110712/5ea4e23b/attachment.html>


More information about the es-discuss mailing list