Design principles for extending ES object abstractions

Luke Hoban lukeh at
Mon Jul 11 22:00:00 PDT 2011

>>>> I agree wholeheartedly with these.  In fact, I'd go further on (2), and say "Anything that can be done declaratively can also be done imperatively, using ES5 syntax".

      >>Like most principles, I think these are reasonable to keep in mind but not absolute. In particular, I see no sensible way to claim that generators can be "done imperatively" in the old language.

My understanding of generators was naively that they are syntactic sugar for defining an iterator.  From that perspective, iterators are the interoperable runtime mechanism that needs to be available in ES5 syntax, and being just duck-typed objects with next(), this appears to be okay.

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.

So I was wrong about iterators being the general interoperable runtime mechanism, but next/send/throw/close objects appear to be fully iterable and consumable in generators.   Is that right?  If so, it seems safe to consider generators as sugar for producing objects whose visible behavior could have been built independently.  And interoperation appears to work cleanly in both directions using these objects.

      >>>> I hope, and believe, there are actually not very many new runtime capabilities being added in that don't already have proposed libraries.  I do think there will need to be some rationalization of the goal to use built-in modules with the reality of ES5-syntax consumers of these libraries.  I'm not sure whether module loaders currently provide a way to do this that would feel accessible.

      >> I agree, but I think this could be done with a minimum of global namespace pollution. For example, let's say we only make the Harmony SystemLoader available to legacy code. That would be enough for ES5 code to:
      - get access to new standard Harmony modules, such as "@name"
      - get access to a Harmony evaluator via SystemLoader.eval
      - get access to user-created Harmony modules
      And it wouldn't require overloading the Object constructor -- from here until eternity -- with a bunch of short-term backwards-compatibility cruft.

I generally like the idea of this.  It may indeed be able to provide convenient and object-detectable ES5 access to new library/runtime functionality.  Reducing the global namespace pollution is certainly a good goal.  This would require that the syntax for loading these standard modules from ES5 is simple and can be implemented efficiently.  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?

      >> But still, I agree with Allen that we should strive where possible to shoot for the goal of making dynamic/reflective analogs of static constructs. For example, Luke has mentioned that he'd like an ability to dynamically construct module instance objects. I think this is a good goal. But not so much for legacy code to have access to it, as for the ability for meta-level code to dynamically interact with base-level code. For example (using totally made-up API's, please don't bikeshed the names):
          childLoader = parentLoader.create(....);
          childLoader.registerModule("m", childLoader.buildModule({
              x: 42,
              f: function() { ... }

Great - agreed that this is a really valuable addition to module_loaders.  A few questions on the example: (1) why is a child loader needed? (2) any particular reason why the buildModule and registerModule are separated?  (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() { ...  }

ASIDE: It still feels a bit odd to see ES5 syntax running on runtime referred to as 'legacy'.  For one thing, it doesn't even exist yet!  But there will be an enormous userbase in this situation beginning in a few years, and probably for many years to come.  Opting into ES6 syntax requires opting into at least strict mode breaking changes and a new script tag.  In contrast, every piece of existing JavaScript code on the web will have the opportunity to object-detect and leverage targeted new runtime functionality.  Module_loaders in particular seems to have a ton to offer to ES5 syntax, by providing a standard means for module definition and consumption for existing JavaScript code, which can be shimmed out to a slower JS implementation where not available.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>

More information about the es-discuss mailing list