Skip to content

Latest commit

 

History

History
156 lines (84 loc) · 11.1 KB

05-13.md

File metadata and controls

156 lines (84 loc) · 11.1 KB

May 13, 2021 Incubator Call Notes (Module fragments)

Attendees:

  • Guy Bedford (GB)
  • Surma (SUR)
  • Shu-yu Guo (SYG)
  • Dan Ehrenberg (DE)
  • Cam Tenny (CJT)
  • Bradley Farias (BFS)

Module fragments

Issues:

DE: (Presents #5)

Main question is whether we want module fragments be based on URL fragments or variables in issue 5. This could also resolve Domenic's issue that it's weird to reuse URL fragments when they already have defined semantics. A module fragment is defined with a variable and the variable is imported. Obviously has to be restricted somehow. Maybe we can do it at parse time with a parallel scope chain, all at "early" time instead of runtime. You can't import something that's normal variable, only if it's declared with a module block.

I think we still want anonymous module blocks but they'd need to be explicitly imported. Nested module blocks should also work, maybe complicated.

(Connection issues, recapping)

There's no URL that addresses the fragment, only the variable.

SUR: (Taking over from Dan's connection issues)

You can't static import a normal binding assigned a module block, e.g. let foo = module {}, but you can dynamic import. There'll be both an anonymous and named form similar to how function declarations work.

Worker integration exists:

module whatev {}
new Worker(whatev);

Interesting thing is we can export identifiers, so you can export module blocks across module boundaries which was not possible before.

GB: What does import * from foo mean when foo is a module foo {}? Is it meant to be import * as?

DE: Yeah, we want import * as.

BFS: Any reason to not allow naming in expression form? How to have modules passing ref to self?

SUR: No, no reason, just like for function forms.

We're thinking the overlap between module blocks and module fragments proposals is sufficiently large and with sufficiently small differences that we should merge the proposals. There's now a symmetry between function declaration forms and module declaration forms proposed here.

One of the biggest questions is can module blocks be used for bundlers, and the answer to that has been "no", but now this merged proposal can be used for bundlers. With this proposal, one module can reference another module block in the same syntactic scope and this would work for bundlers.

DE: I want to add, the previous module fragments proposal was restricted where it could only be used within a module that its own URL. That limited the deployability. This version should work fine even within a script or an eval or any sort of nested lexical scope. Should increase flexibility. One of the reasons for module blocks be a different proposal was that module blocks was context independent while module fragments were not, and this change brings that independence to module fragments.

SUR: Interested in feedback, if this actually solves the problem that it claims it solves. Idea would be we'd write some spec text, at an upcoming TC39 meeting we'd ask for a renewed stage 2.

DE: I'd characterize the change as large, it'd be a lot of change to write up the static scope thing. We'd ask for consensus for stage 2.

SUR: Though amount of syntax added is small.

DE: One question I want to ask is does this early time scoping seem excessively complex? That was the reason I personally was shying away from it. Both complex in the mental sense: now when you see an identifier you'd have to know if it's static or dynamic. And in the the mechanical sense.

GB: Is this fundamentally different than how function names work?

DE: Yeah, pretty different. This is hoisted during linking. Functions are hoisted during the start of the evaluation stage. This has to be hoisted during linking because a export * might need to re-export stuff so you have to resolve all these names before linking.

GB: Wouldn't you do it with a second linking stage (??)

DE: No I think we'd do it in a single linking stage. In HTML you have an integrated fetch/parse/identify dependencies and then go fetch again. We'd have to extend those algorithms to process these things.

GB: It seems like a second phase, is there a declarative way to instantiate them?

DE: Yes, with a static import. Dynamic import also works.

BFS: (repeats question from above)

DE: I don't understand the second part.

BFS: Right now you have import.meta.url that people pass around as a reference to get a hold of the current module, though not the case on the web though people still do it. Would be nice to have a real way to pass around "module this". With "super" I was concerned that it'd just be dangling there, especially for the top-level where it would be invalid. With "module.super" it seemed at least you couldn't make that mistake (??). Having it be the super identifier makes it awkward.

DE: I think you're proposing something different. The super concept right now is currently exactly about the outer module when you have nested module. The self concept you're proposing seems like something else.

BFS: I can explain more. So you have new Worker with the module identifier as an example. How, as a nested module, do I do new Worker(super module)?

DE: My POV is to leave both super and self out of scope for this proposal and pursue them separately.

BFS: I'm pursuing them separately but we need to ensure they're preserved.

DE: Gus's proposal didn't reserve super as an expression. We could do that but that seems narrowing. We could have import.meta something but then it'd be hard for static import to target it (??) We couldn't really use module but module isn't a keyword.

SUR: From a web dev POV I can't come up with anything where super would be interesting to use. Not saying there're no use cases just I don't have any. As you say BFS import.meta.url doesn't guarantee your own module. Seems like an open problem. Agree with DE this is an orthogonal problem, don't want to reserve anything here in time since there are more open questions. Lot more to explore here, if anyone has use cases for super maybe we should have a separate issue.

BFS: The bigger point is we want a reserved (syntax?) area if we have special needs.

SUR: Import.meta?

BFS: No, do not use import.meta. Import.meta is a runtime mutable value, not a static thing.

DE: Import.meta makes sense from a semantic perspective but I agree with BFS's point. We're going to be doing static import from this new static land we're designing. Mixing import.meta into that will be weird because it'll be mutable.

GB: Just to understand better the split between static and dynamic, SUR still provided the example of an inline module with dynamic assignments. Would those dynamic assignments be references to the declarations that are passed around dynamically? How does that work?

DE: All those references, whether using inline modules as expression or not, evaluate to module blocks.

SYG: It worries me to introduce any kind of new dynamic bindings than we have now. We basically have two kinds of bindings now, we have the normal bindings and also those modules that are declared in the declarative form. Those names, the module static names can be used like module static imports but they cannot be used as normal bindings or they are also usable as normal bindings?

DE: They are also usable as normal bindings.

SYG: But the converse is not true, you cannot use a let binding as a static binding. Semantically I’m concerned about - do we extend hosting to these namespace bindings. If I have import * from foo and then we have foo declared at the bottom of the file, is that supposed to work.

DE: In my opinion that is supposed to work, at least in strict mode function declarations.

SYG: So we have a parallel static scope chain, where to your nearest lexical scope that kind of hoisting still happens?

DE: I think this static scoping does seem scary semantically, but I think from an implementation perspective it shouldn’t be harder than for example duplicate let declarations or various properties of field and private method usage.

SYG: If the thing that you require semantically also requires a separate parsing phase then that would be an issue, but as long as it’s possible to implement it without that then I suppose it is ok. As a developer I don’t like needing to know the colour of your identifiers but I would have to think about that.

BFS: One framing that might be helpful is that in general I like to think that when you export a binding you’re adding a symbol to it that it is exported and they are declarative. We do have a similar thing going on in ESM where hoistable declarations are allocated early at link time.

DE: I don’t know if the difference between them being allocated at link time is observable, but these are a little bit earlier than the hoisted declarations.

BFS: You can in a cycle.

DE: Right, the cycle case needs to be handled. But I think these need to be allocated earlier as it has to be handled before everything is fetched.

I think these static module bindings need to be a parallel data structure.

GB: I think it's incredibly exciting, actually. Other complexity I was thinking of: you would be able to nest these things. Do they have shadowing like normal variable declaration?

DE: I think so, just like #-names shadow.

SYG: I don’t know if you saw the issue in the Realms issue, but the concern with module maps could be alleviated by separating the module maps into separate maps. That kind of structure seems like it might also be needed here to instantiate multiple times.

DE: One extra piece of complexity about named exports like import X.A. I think this should work.

BFS: Why allow this instead of allowing someone to import {A}? My concern is we're adding static expression evaluation -- X.A is always done at run time. We do have bindings checked across linkage, but not properties checked.

SYG: Strong agree with BFS.

DE, SUR: We can prohibit this as a starting point.

SYG: Semantically seems quite nice for 80% use case. Don't know implementation implications for changes to HTML's fetch-and-link descendants algorithm.

BFS: Same, don't know how to do this with host hooks yet. Maybe just need to change them.

GB: Seems like this might short-circuit HostResolveImportedModule, now that 262 can just fill it in sometimes?

BFS: We should short-circuit everything we can.

SYG: The corollary concern is that there are now many corner timing cases. Adding a new phase like behaviour to the fetching and parsing behaviour could add more timing behaviours. I am concerned with the proliferation of more web compat stuff we have to maintain into the future.

BFS: My assumption is that this wouldn’t add a new phase except in parsing. Really what’s going on in fetching right now is we have strings and import assertions in the host hook. Now instead of the referrer always being a string it can be some sort of reference to these module blocks but other than that it shouldn’t change anything.

SYG: There’s a lot of cross-cutting things and ongoing optimization work in the implementation I just want to be sure this isn’t going to throw another wrench in.

DE: Thanks for following up there. We’re out of time, thanks everybody for meeting today.