Module Loader Comments

# Kevin Smith (13 years ago)

(Referencing harmony:module_loaders)

When creating a custom loader, I believe there ought to be a way to fallback to using the parent loader's fetching behavior from within the fetch hook. After all, you might want to only override the parent loader's fetching behavior for a certain subset of resolved URLs.

If that feature is available, then I think we should consider eliminating the translate hook and instead allow translation semantics to be defined from within the fetch hook.

  • Aesthetically, we should keep the module loading kernel as small as possible. Translation is really independent from module loading and we should probably push the concept of translation up to a higher layer if possible.

  • The current translate hook only provides URLs as input. This might not be sufficient in some cases to determine the appropriate translation. For example, we might want to use the value of a "Content-Type" HTTP header. In such a scenario, the natural place to define that branching logic would be in the fetch hook, after retrieving the file using (X-Domain) HTTP requests.

Thoughts? I could be missing an obvious counter-argument, of course ; )

# Sam Tobin-Hochstadt (13 years ago)

On Tue, Mar 12, 2013 at 9:12 AM, Kevin Smith <khs4473 at gmail.com> wrote:

(Referencing harmony:module_loaders)

When creating a custom loader, I believe there ought to be a way to fallback to using the parent loader's fetching behavior from within the fetch hook. After all, you might want to only override the parent loader's fetching behavior for a certain subset of resolved URLs.

The way to invoke the default behavior will be to just return undefined from a hook (or fulfill with undefined for the fetch hook). We'll be updating the wiki in the next week or two with these sorts of issues.

If that feature is available, then I think we should consider eliminating the translate hook and instead allow translation semantics to be defined from within the fetch hook.

This won't work, for several reasons:

  • It ought to be possible to override the translation behavior without mucking with fetching. Coffeescript translators don't need to perform their own fetching.
  • calls to eval go through the translate hook, but not through the fetch hook, since there's nothing to fetch.
  • Aesthetically, we should keep the module loading kernel as small as possible. Translation is really independent from module loading and we should probably push the concept of translation up to a higher layer if possible.

As simple as possible, but no simpler. Loading is about how to turn a reference to a module into the bytes of a JS program, and translation fits right in there. I'm not sure which other layer you're thinking of.

  • The current translate hook only provides URLs as input. This might not be sufficient in some cases to determine the appropriate translation. For example, we might want to use the value of a "Content-Type" HTTP header. In such a scenario, the natural place to define that branching logic would be in the fetch hook, after retrieving the file using (X-Domain) HTTP requests.

Passing along this additional data may well be a good idea. We plan to make basically all the additional arguments to each hook into an options object, and this would fit there. Note that it wouldn't be available always, though -- witness the case of eval.

# Kevin Smith (13 years ago)

The way to invoke the default behavior will be to just return undefined from a hook (or fulfill with undefined for the fetch hook).

That's one way to do it. It doesn't allow the overrider to do any transformation on the result though. If the overrider can explicitly call the default behavior, then source translation could just be done from within the fetch hook.

We'll be updating the wiki in the next week or two with these

sorts of issues.

The sooner the better! Modules are (IMO) the most important new feature of ES6 and it's getting close...

  • It ought to be possible to override the translation behavior without

mucking with fetching. Coffeescript translators don't need to perform their own fetching.

First, if the default behavior is explicitly invoked (as opposed to implicitly by returning undefined), then fetching doesn't really have to be mucked with.

Also (and this is a separate point), I don't see how in the current design a CoffeeScript translator could be implemented as a custom loader. As far as I can tell, all loaders encapsulate their own module instance table. For CoffeeScript, you want the JS module instance table and the CS module instance table to be the same. Is there a way to have a custom loader share an instance table with another loader, then?

  • calls to eval go through the translate hook, but not through the fetch hook, since there's nothing to fetch.

So we're going to have eval execute arbitrary languages other than javascript? I didn't realize this... I'm going to have to think on that for a while.

Thanks for debating, BTW!

# Sam Tobin-Hochstadt (13 years ago)

On Thu, Mar 14, 2013 at 11:03 AM, Kevin Smith <khs4473 at gmail.com> wrote:

The way to invoke the default behavior will be to just return undefined from a hook (or fulfill with undefined for the fetch hook).

That's one way to do it. It doesn't allow the overrider to do any transformation on the result though. If the overrider can explicitly call the default behavior, then source translation could just be done from within the fetch hook.

That's why for several of the hooks, we plan to provide to the hook an explicit value which is what the default would do. For translate this isn't needed, since the default is not to transform, and for fetch browsers already provide ways of doing remote fetching. I don't think we want to force engines to reify these operations as new JS functions.

We'll be updating the wiki in the next week or two with these sorts of issues.

The sooner the better! Modules are (IMO) the most important new feature of ES6 and it's getting close...

Glad you're excited about modules.

  • It ought to be possible to override the translation behavior without mucking with fetching. Coffeescript translators don't need to perform their own fetching.

First, if the default behavior is explicitly invoked (as opposed to implicitly by returning undefined), then fetching doesn't really have to be mucked with.

Also (and this is a separate point), I don't see how in the current design a CoffeeScript translator could be implemented as a custom loader. As far as I can tell, all loaders encapsulate their own module instance table. For CoffeeScript, you want the JS module instance table and the CS module instance table to be the same. Is there a way to have a custom loader share an instance table with another loader, then?

For this use case, you'd probably want to just modify the default System loader to understand Coffeescript, via file extension detection, AMD-style plugins, or some other mechanism.

If you want a new loader, you'll have to explicitly share the modules that you want to share. But the point of new loaders is to have the distinct table.

  • calls to eval go through the translate hook, but not through the fetch hook, since there's nothing to fetch.

So we're going to have eval execute arbitrary languages other than javascript? I didn't realize this... I'm going to have to think on that for a while.

For Coffeescript, it's easy to actually translate eval calls.

The more important reason to handle eval in a loader is so that if you're enforcing some invariant on code executed in a specific loader, you don't want the code to be able to escape that invariant via eval.

Thanks for debating, BTW!

Hopefully it doesn't need to be a debate. :)

# Brandon Benvie (13 years ago)

On 3/14/2013 11:03 AM, Kevin Smith wrote:

Also (and this is a separate point), I don't see how in the current design a CoffeeScript translator could be implemented as a custom loader. As far as I can tell, all loaders encapsulate their own module instance table. For CoffeeScript, you want the JS module instance table and the CS module instance table to be the same. Is there a way to have a custom loader share an instance table with another loader, then?

You can't share the table, but you can manually use loaderOne.set(mrl, loaderTwo.get()) after the translate? (I think this would be closest to the final evaluation) hook.

# Brandon Benvie (13 years ago)

On 3/14/2013 12:04 PM, Brandon Benvie wrote:

loaderOne.set(mrl, loaderTwo.get())

Sorry, I meant loaderOne.set(mrl, loaderTwo.get(mrl)). Assuming their resolve hooks are the same I guess.

# Kevin Smith (13 years ago)

The more important reason to handle eval in a loader is so that if you're enforcing some invariant on code executed in a specific loader, you don't want the code to be able to escape that invariant via eval.

This makes sense now. The loader has the opportunity to analyze and transform external code, so it should also have the same ability with respect to dynamic code. Maybe this rationale could be captured on the wiki.

So it's really more of a "transform" hook than a "translate" hook. Would naming it "transform" would make the intention more obvious?

# Sam Tobin-Hochstadt (13 years ago)

On Thu, Mar 14, 2013 at 2:14 PM, Kevin Smith <khs4473 at gmail.com> wrote:

So it's really more of a "transform" hook than a "translate" hook. Would naming it "transform" would make the intention more obvious?

Transformations of languages are translations, so I think that translate is clearer. CS -> JS is a translation.

# Kevin Smith (13 years ago)

So it's really more of a "transform" hook than a "translate" hook. Would naming it "transform" would make the intention more obvious?

Transformations of languages are translations, so I think that translate is clearer. CS -> JS is a translation.

Sure, but to me "translate" implies translation from one programming language to another. But the "loader" concept can be fully expressed without dependence on on the concept of "other languages". "Transform", to me, implies the more generic concept.

Anyway, my questions are cleared up (for now). Can't wait for those wiki updates! ; )

# Kevin Smith (13 years ago)

Anyway, my questions are cleared up (for now). Can't wait for those wiki updates! ; )

Ah - one more comment.

The signatures for load and evalAsync indicate that they will return this, a reference to the loader. These functions are prime candidates for returning futures (e.g. something with a then method) in some future version of ES with standardized promises/futures. Would it be more future-safe to leave the return values of these methods undefined for the present?

# David Herman (13 years ago)

That sounds reasonable to me.