09:00 | <shu> | what is the last item scheduled for today? |
09:03 | <Rob Palmer> | ShadowRealm |
09:04 | <Rob Palmer> | but it may move slightly if time magically opens up |
09:05 | <shu> | i should like to be present for shadowrealms but tbqh i don't know if i'll be able to stay awake until then |
09:08 | <ljharb> | * can someone let me in to the meet? Rob Palmer ryzokuken bterlson |
09:09 | <Luca Casonato> | bakkot: can you add amazing iterator -> async iterator ? |
09:19 | <ljharb> | bterlson: advance queue? |
09:23 | <Rob Palmer> | As Michael Saboff said, please add your name, abbreviation and organization to today's meeting notes. Link here: https://github.com/tc39/Reflector/issues/446 |
09:24 | <Rob Palmer> | Please could everyone avoid sharing direct URLs to our notes or meeting URLs in this publicly logged room. These are all linked from the Reflector post so that they are kept private. |
09:28 | <Michael Ficarra> | already 15 min ahead! |
09:37 | <Christian Ulbrich> | Wouldn't this be also the first time, that we would have a ~keyword~operator being extended / giving a . ? await is not a normal function but sth. magical like delete ? |
09:38 | <ljharb> | no, we have new.target |
09:38 | <ljharb> | and super.whatever |
09:38 | <rbuckton> | and import.meta |
09:38 | <ljharb> | metaproperties are already A Thing |
09:40 | <Christian Ulbrich> | super.whatever is used like a magical function and new.target is a magical property. import.meta a magical global. |
09:40 | <yulia> | speaking of research -- anyone wanna take over running that call? |
09:42 | <Christian Ulbrich> | I can relate to the idea of hiding Promises, but it would come at the cost of having - IMO - at the cost of another magical thing, that we do not have so far. |
09:44 | <ljharb> | it could be an awaitAll keyword and it'd be the same |
09:44 | <yulia> | we have issues with sound in the room? |
09:44 | <ljharb> | await.all isn't magical, it's just a way to nest keywords under keyords in this case |
09:44 | <bakkot> | await.all [a, b, c] just doesn't seem that different from await Promise.all([a, b, c]) to me |
09:45 | <rbuckton> | async/await isn't about hiding promises. It's about taking continuation passing style code and allowing you to write it as linear code so that you can maintain the benefit of existing control flow constructs like continue /break /return /yield . |
09:45 | <yulia> | what is being highlighted right now is the issue of breaking consistency in a language -- new developers will have a hard time keeping two things in mind |
09:45 | <ljharb> |
await a; await b; await c constantly |
09:45 | <yulia> | I would question teaching async/await before promises for this reason |
09:45 | <yulia> | i don't think the solution here is syntax |
09:46 | <Christian Ulbrich> | In other words, is there so far any await -like thing with a . ? |
09:46 | <ljharb> | Christian Ulbrich: no, in that respect it'd be something new, but almost every new syntax feature has that :-) |
09:46 | <ljharb> | yulia: i totally agree this doesn't fix the education problem. to me it's fixing an ergonomics problem. |
09:46 | <Alex Vincent> | DevMo has a great example of how to do promises in sequence via Array.prototype.reduce |
09:46 | <bakkot> | ljharb I definitely see people do await a; await b; await c over await Promise.all but I have generally assumed it was because of thinking serially rather than because of wanting to avoid Promise ? |
09:46 | <yulia> | yulia: i totally agree this doesn't fix the education problem. to me it's fixing an ergonomics problem. |
09:46 | <bakkot> | "wanting to avoid Promise " seems like a problem we should not be trying to solve for them |
09:46 | <yulia> | you just did so as well |
09:46 | <Kris Kowal> | I imagine for await all would take a lot of pressure off of await.all . |
09:46 | <yulia> | i don't agree that this is a solution |
09:47 | <ljharb> | ljharb I definitely see people do |
09:47 | <Christian Ulbrich> | Motivate a change to "unconfuse" developers from Promises by adding a totally new syntax, should be well-thought of. |
09:47 | <shu> | ljharb I definitely see people do |
09:48 | <shu> | i reckon beginners are not at a place that would reach for parallel combinators but for a more discoverable way to do so, like syntax, or something |
09:48 | <shu> | they're just not at a place to reason about things in parallel |
09:48 | <Christian Ulbrich> | Concerning the hiding motivation, it actually is a problem that have all the time with junior developers, they already do not know, that they are using promises... |
09:48 | <Kris Kowal> | I agree with @yulia that promises should be taught before async/await. I agree with rbuckton that the latter is not to hide the former. |
09:49 | <HE Shi-Jun> | if the main problem is await all, should we only add await.all without await.race and others? Or even just introduce a special syntax (eg. await* promises )? |
09:49 | <shu> | first we must teach one of the First Mistakes -- the existence of the microtask queue |
09:49 | <Christian Ulbrich> | shu: The whole Playwright API is heavily async... |
09:49 | <shu> | Christian Ulbrich: sorry what's the context of that comment? i don't know what that means |
09:51 | <ljharb> | if the main problem is await all, should we only add |
09:51 | <Alex Vincent> | "do nothing" is always an option. |
09:51 | <HE Shi-Jun> | ljharb: if syntax, there is no inconsistency? |
09:52 | <ljharb> | the inconsistency would be 3 of the promise combinators lacking syntax, but 1 having it |
09:55 | <HE Shi-Jun> | I mean, if 80% usage of combinators is just Promsie.all, is it ok to only add syntax for it? |
09:58 | <ljharb> | should we only add a subset of + - * /, because some of them get more usage than the others? |
09:58 | <yulia> | i remain adamant that consistency is important for learnability of a language, 20% is a pretty big chunk, but i think we are just pulling numbers from the air right now. |
10:00 | <Jack Works> | speaking of research -- anyone wanna take over running that call? |
10:00 | <yulia> | Sad to see you cannot attend |
10:00 | <yulia> | you can bug felienne |
10:00 | <yulia> | or we can go without a research call for a while |
10:10 | <ljharb> | bterlson: it'd be really helpful to plan my sleep if the hackmd could be updated with an estimated plan for the rest of the day 🙏 |
10:10 | <Jack Works> | they're just not at a place to reason about things in parallel |
10:11 | <Rob Palmer> | ljharb: hackmd is constantly kept as up to date as we can make it - as we approach the end of the meeting it generally becomes more reliable as uncertainty decreases |
10:11 | <Kris Kowal> | I think Promise.all is the tip of the lazy iceberg. To go from lazy about parallelism to rigorous usually means converting a for loop to a map, or accumulating an array of promises in a for loop. |
10:11 | <ljharb> | thanks, appreciate whatever you can do! |
10:12 | <shu> | Jack Works: have you tried to use editor abbreviations? |
10:12 | <Jack Works> | "do nothing" is always an option. |
10:12 | <shu> | like this seems like an easier problem to solve than through the language |
10:13 | <Kris Kowal> | A much bolder improvement would be parallel for await loops. |
10:15 | <Jack Works> | should we only add a subset of + - * /, because some of them get more usage than the others? |
10:15 | <rbuckton> | OOT: imo some operators (| & %) do not deserve a single character operator because they're not used so often (than || &&) |
10:16 | <Kris Kowal> | OOT: imo some operators (| & %) do not deserve a single character operator because they're not used so often (than || &&) |
10:16 | <sffc> | I would like 10 minutes of plenary time to discuss an additional question regarding Intl NumberFormat v3. I have added a slide at the end of the deck I used on Tuesday |
10:16 | <Jack Works> | wanna take over? |
10:16 | <apaprocki> | sffc: have you proposed strings for the eras anywhere? |
10:17 | <yulia> | i think the only person who really knows what they are doing is felienne |
10:19 | <shu> | i know how to confirm my priors and ignore counter evidence |
10:19 | <Jack Works> | Jack Works: have you tried to use editor abbreviations? |
10:20 | <Jack Works> | One of the main pain point is when I'm using for...of loop and I want to use await in the loop body |
10:20 | <yulia> | i know how to confirm my priors and ignore counter evidence |
10:21 | <sffc> | apaprocki: All context and work on this should be available on the proposal repository; e.g. https://github.com/FrankYFTang/proposal-intl-era-monthcode/issues/1 ... my colleague Manish has a proposal that has been presented to CLDR. There is still room to evolve before they are written down. Would like your feedback if interested! |
10:21 | <Jack Works> | One of the main pain point is when I'm using for...of loop and I want to use await in the loop body |
10:22 | <rbuckton> | slides aren't showing on meet |
10:22 | <yulia> | blank screen |
10:22 | <HE Shi-Jun> | I can't see the slide |
10:26 | <Jack Works> | * you don't need to understand what membrane is to know the motivation of this proposal |
10:26 | <Jack Works> | TLDR: we have tons of revocable Proxies and we need to revoke them in an ergonomic way. |
10:28 | <rkirsling> | I mean you kind of do if you've never encountered proxy revocation before (I merely know that it's a thing that exists that I've not needed to investigate to any depth) |
10:30 | <Jack Works> | today:
You need to store tons of revoker functions and call them one by one |
10:31 | <rbuckton> | I'm curious if
Or make the return value of |
10:32 | <Jack Works> | Cannot help. |
10:33 | <Jack Works> | Proxy does not have a prototype to store @@disposable |
10:33 | <rbuckton> | I'm not sure how I see how |
10:34 | <ljharb> | the confusing name of defer aside, that seems like it'd work |
10:34 | <Christian Ulbrich> | Is the memory argument really so strong? |
10:35 | <Michael Ficarra> | I don't think we need to determine whether DisposableStack would be sufficient today |
10:35 | <Michael Ficarra> | this can reach stage 1 and later we don't advance to stage 2 because we realise DisposableStack is a fine solution |
10:36 | Christian Ulbrich | has never revoked a proxy, why would I? I want all of them to go through my proxy :) |
10:37 | <HE Shi-Jun> | Proxy does not have a prototype to store @@disposable |
10:37 | <rbuckton> | And I meant that its not the proxy that is disposable, but the { proxy, revoke } object |
10:38 | <HE Shi-Jun> | I think DisposableStack could work, but the proposed api seems much easy to use. |
10:39 | <Christian Ulbrich> | I also had the https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal in mind... |
10:40 | <HE Shi-Jun> | yeah, cross realm seems a requirement of this proposal? |
10:41 | <Mathieu Hofman> | I think wrapped revokers could also be optimized in theory, but would any engine realistically perform all these optimizations? |
10:42 | <Christian Ulbrich> | Proxies now have some heavy uses... they are used by recent Vue3 and libs implementing immutability. |
10:42 | <Christian Ulbrich> | I still wonder though, whether they are actually revoking their Proxies... |
10:42 | <HE Shi-Jun> | Not sure whether Vue3/Mobx use revoke... |
10:43 | <rkirsling> | hard to disagree with shu here as my point above was that I've never actually used a Proxy, let alone thought about revoking one |
10:43 | <Mathieu Hofman> | you may not have used one directly, but lots of libraries / frameworks do leverage them under the hood |
10:43 | <rkirsling> | (not that I'm objecting to anything :P my point above was that without membranes, there isn't an obvious context of usage) |
10:44 | <Jack Works> | yeah, cross realm seems a requirement of this proposal? |
10:44 | <Mathieu Hofman> | I would like to see usage numbers from web browsers (which do not cover the usages in non browser environments) |
10:44 | <Jack Works> | And I meant that its not the proxy that is disposable, but the |
10:45 | <Christian Ulbrich> | Membranes are just a mental model around Proxies. There are many use cases for Proxies, it does not matter, whether they are actually membranes. |
10:47 | <shu> | Vue is proxying objects to implement immutability? |
10:47 | <shu> | they expect this to be performant? |
10:48 | <Christian Ulbrich> | @shu They use it to implement reactivity. |
10:48 | <HE Shi-Jun> | Vue is proxying objects to implement immutability? |
10:49 | <Christian Ulbrich> | I have skimmed through the source, looks like they never revoke them. |
10:49 | <shu> | interesting, i see |
10:49 | <Jack Works> |
|
10:51 | <rkirsling> | so do we have a non-theoretical go-to use case of revocation then? |
10:52 | <Christian Ulbrich> | rkirsling: So far not, because at least Vue, does not seem to use revocation |
10:52 | <Mathieu Hofman> | Jack Works: according to the definition of liveness, clearing out the proxy target is not necessary, as the target's identity would be unobservable to the program through the proxy already |
10:52 | <rbuckton> |
new Proxy(target, handlers, { revocationStack: disposables }) might be feasible, but prefer this approach in general |
10:53 | <Jack Works> | Jack Works: according to the definition of liveness, clearing out the proxy target is not necessary, as the target's identity would be unobservable to the program through the proxy already |
10:54 | <Mathieu Hofman> | so do we have a non-theoretical go-to use case of revocation then? |
10:55 | <Mathieu Hofman> | the [[ProxyHandler]] might get access to [[ProxyTarget]] when the internal slot is called right |
10:57 | <Mathieu Hofman> | An editor note might suffice to make it clear that once revoked, the proxy target's identity will no longer be observable by the program through the handler / proxy |
10:57 | <Luca Casonato> | Thanks bterlson!! 👏 |
10:59 | <Rob Palmer> | We will resume at 13:00 (62mins time) |
10:59 | <Christian Ulbrich> | I assume that SalesForce' whole LightningLocker stuff being based on Proxies, they are also using revocation |
10:59 | <bakkot> | if anyone here is on the typescript team and needs something to do over lunch I have a small QoL PR https://github.com/microsoft/TypeScript/pull/51457 |
11:00 | <Michael Ficarra> | I'm realising now that I have a LOT of tests to write for iterator helpers in test262... |
11:00 | <Michael Ficarra> | btw, link to the thread asking for a TG3 chair that I mentioned: https://github.com/tc39/Reflector/issues/450 |
11:00 | <bakkot> | yeah set methods will be fun too |
11:01 | <Michael Ficarra> | 🙏 hopefully a helpful community member will just do it before I get around to it |
11:08 | <ljharb> | i'm going to sleep because i'm sick and exhausted, but for Resource Management, i still think https://github.com/tc39/proposal-explicit-resource-management/issues/102 is not resolved, and i think any stage 3 for it needs to be conditional on coming to consensus on that. i hope my absence won't mean my concerns are overlooked (assuming that it otherwise would get stage 3, ofc) |
11:09 | <shu> | ljharb: is there a summary of your concern? |
11:10 | <shu> | or can ron characterize it accurately? |
11:13 | <rbuckton> | IIRC, it's in regards to argument ordering of the adopt method, and thus it's existence entirely |
11:15 | <rbuckton> | I can add a note to the slides to call this out |
11:17 | <shu> | great, thanks |
11:21 | <Alex Vincent> | I missed a key point in my presentation: when we revoke a proxy, the underlying slots are nullified, but the proxy itself remains part of the object graph it lives in. You can't do anything with the proxy, but it's still there because (probably) something else refers to it. The revocation of an object graph, and of proxies pointing to it, means the original objects in that object graph can be gc'd. The proxies themselves are still reachable |
11:57 | <Alex Vincent> | ljharb: Rob Palmer per the proposals index, I have transferred my GitHub repo to tc39-transfer. |
12:03 | <Mathieu Hofman> | Markm is still having trouble joining |
12:04 | <Rob Palmer> | tell him to try again |
12:04 | <Rob Palmer> | I think I have admin rights to let people in now |
12:04 | <Mathieu Hofman> | He did |
12:05 | <Mathieu Hofman> | Thanks! |
12:06 | <Rob Palmer> | It wasn't me - I suspect Yulia has done it |
12:06 | <yulia> | nope -- it was ujjwal |
12:06 | <yulia> | i don't have rights either |
12:06 | <yulia> | from the shadows |
12:07 | <ryzokuken> | Permissions fixed |
12:08 | <Mathieu Hofman> | I missed a key point in my presentation: when we revoke a proxy, the underlying slots are nullified, but the proxy itself remains part of the object graph it lives in. You can't do anything with the proxy, but it's still there because (probably) something else refers to it. The revocation of an object graph, and of proxies pointing to it, means the original objects in that object graph can be gc'd. The proxies themselves are still reachable |
12:21 | <yulia> | dumb question about this -- this would still be usable with streams in spite of the lack of async or no? |
12:22 | <bakkot> | yulia: basically no |
12:22 | <bakkot> | if the dispose method is async you need the async syntax, which is not in this version of the proposal |
12:22 | <yulia> | what are current use cases that would be directly impacted here? |
12:22 | <bakkot> | and stream.cancel is async |
12:24 | <bakkot> | https://github.com/tc39/proposal-explicit-resource-management/#relation-to-dom-apis has a list |
12:24 | <yulia> | I'm aware of the the atomic locking/unlocking behavior, but i think this is also in support of the shared structs proposal? |
12:24 | <bakkot> | about half of which is sync stuff |
12:24 | <yulia> | ah great thanks |
12:24 | <bakkot> | also I really like https://github.com/whatwg/html/issues/8557#issuecomment-1331448189 |
12:25 | <yulia> | thanks, that is useful |
12:32 | <Ashley Claymore> | also I really like https://github.com/whatwg/html/issues/8557#issuecomment-1331448189 adding in do-expressions to this would be nice, for ensuring the cancelation happens asap.
|
12:32 | <bakkot> | Ashley Claymore: that doesn't actually end up making any difference |
12:33 | <Ashley Claymore> | ah yes, Im thinking of Promise.race |
12:33 | <Ashley Claymore> | where it would |
12:33 | <bakkot> | ah, yeah |
12:47 | <Michael Ficarra> | Rob Palmer: is VERY quiet |
12:57 | <bakkot> | I should say also that I do not at all agree with markm's opinion that { using async x = y } causing an await at block exit is a problem |
12:57 | <bakkot> | I would much prefer to just use that syntax |
12:59 | <bakkot> | it's not like it's super nested - the using statement is necessarily at the same level as the block where the await will happen; even if it's in the middle of the block, being at the top level of the block means it is inherently not that obscure |
13:00 | <Kris Kowal> | I tend to agree. Maybe I can help convince markm. |
13:02 | <shu> | +1 the partial class initialization thing is a pretty good motivation imo |
13:08 | <Mathieu Hofman> | bakkot: in that short example maybe, but 1) currently all interleaving points within an async function are marked with await , and 2) with a block populated with other statements, the using async would not be as visible |
13:10 | <bakkot> | I really don't think it would be hard to see the using async , and I consider it to be a sufficient marker of the interleaving point |
13:10 | <Mathieu Hofman> | we have found nested async interleaving to be a footgun today, and we're considering any further unclear interleaving as very undesirable |
13:11 | <bakkot> | I consider making using async harder to use very undesirable |
13:11 | <Mathieu Hofman> | async is not a marker of interleaving. await is |
13:11 | <shu> | wait i don't understand why "async" isn't a clear indication of an interleaving point? |
13:12 | <shu> | well it's a marker that the thing contains interleaving points? |
13:12 | <Mathieu Hofman> | async is a declaration that the thing has asynchronous behavior (returns a promise) |
13:12 | <shu> | yes, you're declaring a thing that can contain interleaving points |
13:12 | <sffc> | Congratulations rbuckton on Stage 3! I'm looking forward to using the proposal in the wild 😀 |
13:12 | <Mathieu Hofman> | no you're declaring it returns a promise |
13:12 | <Mathieu Hofman> | await is the interleaving |
13:13 | <eemeli> | rbuckton: I would still appreciate seeing what your PluginHost example would look like if DisposableStack was not available. |
13:13 | <caridy> | trying to join the call! can someone let me in? |
13:13 | <bakkot> | Mathieu Hofman: I just don't think the "it has to be exactly the await keyword, and not the async keyword" is a rule which is important |
13:13 | <bakkot> | who is that rule for? |
13:14 | <Mathieu Hofman> | for people reading / auditing a piece of code |
13:14 | <bakkot> | right but like |
13:14 | <bakkot> | why is async not sufficient for those people |
13:14 | <shu> | more specifically why is using async insufficient |
13:14 | <Mathieu Hofman> | because that's what async means today? |
13:14 | <shu> | treat it as a lexeme |
13:14 | <shu> | people are smart and adaptable |
13:15 | <rbuckton> | wait i don't understand why "async" isn't a clear indication of an interleaving point? await and yield are the only way to indicate interleaving points. async does not, since you must await the result of the async function to observe the result. |
13:15 | <bakkot> | yeah, what shu said |
13:15 | <bakkot> | right and I'm disagreeing with erights |
13:15 | <Mathieu Hofman> | people are smart and adaptable |
13:15 | <bakkot> | I think using async being a new way to indicate an interleaving point is not a problem |
13:15 | <bakkot> | it's fine |
13:15 | <shu> | like this strikes me as "we must ship my mental model or we ship nothing", which doesn't seem great? |
13:15 | <caridy> | Rob Palmer: can you help? trying to join the call. |
13:16 | <Mathieu Hofman> | I don't see why we're so willing to throw away the consistency of the language we have today |
13:16 | <caridy> | thanks |
13:16 | <Rob Palmer> | done |
13:16 | <bakkot> | because it's not an important consistency, especially when weighed against the cost of only being able to put using async inside if statements |
13:16 | <bakkot> | the second thing is much, much more important |
13:17 | <rbuckton> | right and I'm disagreeing with erights The relevant discussion can be found in https://github.com/tc39/proposal-explicit-resource-management/issues/101. This argument has been explicitly called out here: |
13:17 | <shu> | in general there are many surface consistency things we ought to trade off against other things |
13:17 | <shu> | argument order and coercion order and so forth is often another one |
13:17 | <Mathieu Hofman> | we consider await in conditional statements to be a footgun, so that doesn't have much weight in our opinion |
13:17 | <bakkot> | well |
13:17 | <bakkot> | that's... |
13:17 | <bakkot> | uh. |
13:17 | <bakkot> | not an opinion I agree with. |
13:18 | <shu> | well my response to that is, sorry you don't get to ship an implicit linter in the language |
13:18 | <bakkot> | you are welcome to not put await in conditional statements but I really do not approve of you designing the language to stop me from doing it |
13:18 | <shu> | yes |
13:18 | <rbuckton> | I don't understand that position either. await in a conditional doesn't "release zalgo", so such a constraint seems confusing. |
13:18 | <Mathieu Hofman> | right, I think we fundamentally disagree with the ability to reason about interleaving points. I don't see why an explicit block marker is so onerous |
13:19 | <Mathieu Hofman> | I don't understand that position either. PromiseResolve |
13:19 | <rbuckton> | well my response to that is, sorry you don't get to ship an implicit linter in the language ?? would like to disagree with you. I was not in favor of requiring parens to disambiguate with || and && given clear precedence rules, but here we are. |
13:20 | <rbuckton> | No, "releasing zalgo" is writing an API such that consumers cannot safely reason over whether that API will execute synchronously or asynchronously. |
13:21 | <rbuckton> | If the function is async, it will always complete in a later turn, but will start executing in this turn. |
13:21 | <bakkot> | Mathieu Hofman I think that most people have not found await in conditional statements to actually be a problem. so I think if you are reasoning about your code in a way where it is a problem, the appropriate way for you to enforce that is by writing your own linting rules, which forbid both await in conditional statements and using async in blocks, and let the rest of us have both of those things |
13:23 | <Mathieu Hofman> | anyway, I think we deviated a little, and the conditional thing is not that relevant to this discussion. async using is a declaration that the value has an async behavior. It does nothing to indicate that there will later, at the end of the current scope, be an interleaving point, which are today all marked with an explicit await keyword |
13:23 | <bakkot> | it's a new piece of syntax, and you have to learn what it means |
13:23 | <Mathieu Hofman> | Mathieu Hofman I think that most people have not found |
13:23 | <bakkot> | I really don't think it's that hard to learn |
13:24 | <bakkot> | Mathieu Hofman: I accept that you have, but stand by my statement, and my position that the appropriate resolution for you is to have lint rules which enforce the stronger invariants that you apparently need; most people, in my experience, do not need them, so we should not bake them into the language. |
13:26 | <Mathieu Hofman> | again I'm simply asking for the language to stay consistent with itself and mark interleaving points at the location of interleaving |
13:26 | <bakkot> | and I'm saying I don't think that consistency is worth the cost of making using async harder to use. |
13:26 | <bakkot> | given that it is, in my opinion, really not that hard to learn what using async does. |
13:27 | <Mathieu Hofman> | what's insane to me is that adding a async using statement to a plain block introduces an interleaving point at a distance |
13:27 | <rbuckton> | Mathieu Hofman: I accept that you have, but stand by my statement, and my position that the appropriate resolution for you is to have lint rules which enforce the stronger invariants that you apparently need; most people, in my experience, do not need them, so we should not bake them into the language. await was to use linter to require something like // await using at the end of a block, but that didn't seem to be sufficient at the time. |
13:27 | <Mathieu Hofman> | it's a refactor hazard |
13:27 | <bakkot> | it does not seem like any more of a refactoring hazard than the sync call to Symbol.dispose() is |
13:27 | <bakkot> | or at least not very much more |
13:28 | <shu> | let's take a step back, what is the thing that you are doing that makes you care so much about interleave points? |
13:29 | <Mathieu Hofman> | being able to reason when your synchronous execution has been interrupted, and thus your state may have gotten mutated. |
13:30 | <shu> | why is that not a concern with sync dispose |
13:30 | <rbuckton> | Any time you run code you run the risk of side effects. +value can have side effects. An implicit await would have side effects, but that's far easier to run into than a side-effecty +value or the like.If you're writing code where an implicit async interleaving point could be a problem (i.e., potential state mutations in closed-over code), you have the same problem with any await and would likely need to use some type of async coordination primitive to synchronize access. |
13:31 | <shu> | like i agree this is a problem, which is why i added "can call user code", but that's not specific to async |
13:31 | <rbuckton> | One point in the favor of the explicit marker is that its harder to recognize you might need to coordinate access if the interleave point is implicit. |
13:31 | <Mathieu Hofman> | because you control the resource and what it does. You do not control what other promise jobs may have done if suspended |
13:31 | <shu> | "you control the resource" is in practice, not true |
13:32 | <Jack Works> | some thought for the current problem: |
13:32 | <rbuckton> | I attempted to make the point that a declaration like async using x = ... would be sufficient to indicate such coordination might be necessary. |
13:32 | <Jack Works> | worker.addModule(module {}, import.meta.url) |
13:33 | <Mathieu Hofman> | if your model allows you to trust all the objects you invoke synchronously, you don't have to worry about synchronous re-entrancy |
13:35 | <bakkot> | I think the subset of programs in which people are sufficiently careful that they are able to make and maintain an accurate model which allows them to trust all the objects they invoke synchronously, but where the code is being read so quickly that using async does not suffice as a marker of an interleaving point, is quite small |
13:35 | <shu> | i daresay that subset of programs is exceedingly small |
13:36 | <shu> | Mathieu Hofman: ISTM your position is predicated on a very opinionated environment that does not generalize, and thus should not be baked into the language |
13:40 | <Bradford Smith> | Could someone let me back into the meeting? (My internet cut out, so I got dropped.) |
13:41 | <Bradford Smith> | thx |
13:45 | <Rob Palmer> | a heads up: we are overrunning in this session. therefore it is possible the meeting may end 15 minutes late in order to give shadow realms the full 60mins |
13:46 | <danielrosenwasser> |
|
13:47 | <shu> | hm that's a good data point |
13:48 | <rbuckton> | I get frustrated by it every now and then. |
13:48 | <rbuckton> | So the number is non-zero, even if close to zero. |
13:51 | <danielrosenwasser> | Let it be known that Ron runs into it :D |
13:55 | <snek> | did iterator helpers get stage 3? |
13:56 | <bakkot> | snek: yes it did! |
13:56 | <Ashley Claymore> | https://twitter.com/robpalmer2/status/1598248214750191616?s=20&t=6pFlDUfZtj5hXI1s5l9ohw |
13:56 | <snek> | hypeeee |
13:56 | <Michael Ficarra> | snek: https://mastodon.social/@robpalmer/109437830520093382 |
13:57 | <snek> | ooo now which one do I click on |
14:00 | <Luca Casonato> | Justin Ridgewell: I want to push back on the statement that module expressions are not useful without module declarations. I would argue that module expressions have very strong motivation, separate from motivation for module declaration. |
14:01 | <rbuckton> | Motivations aside, there is so much shared between them its difficult to talk about them independently. |
14:01 | <Luca Casonato> | Motivations aside, there is so much shared between them its difficult to talk about them independently. |
14:01 | <caridy> | the fact that the expressions cannot be used to import from, is sufficient for me to keep them independently. |
14:02 | <Luca Casonato> | I think it is reasonable to talk about expressions without declarations, but not the other way around. |
14:02 | <Luca Casonato> | One can exist and be useful without the other |
14:04 | <Robert Pamely> | Are nested modules scoped to within the parent module? |
14:04 | <Luca Casonato> | Are nested modules scoped to within the parent module? |
14:04 | <Luca Casonato> | Unless you explicitly export them |
14:05 | <Jack Works> | the real reason those proposals are separated IMO because they're initially separated presented to the committee (and back to days they're stage 0, they don't looks similar too much) |
14:05 | <Robert Pamely> | Are modules independently cachable in browsers? If Site A serves lodash, can Site B use that? |
14:06 | <Jack Works> | Are modules independently cachable in browsers? If Site A serves lodash, can Site B use that? |
14:06 | <Luca Casonato> | Ie:
|
14:06 | <Jack Works> | I've heard browsers are shipping cross-site cache isolation to prevent tracking |
14:07 | <ryzokuken> | besides, practically speaking, due to the widespread use of bundlers it's not very likely |
14:07 | <Jack Works> | everyone bundles their own React 🤔 |
14:07 | <Robert Pamely> | besides, practically speaking, due to the widespread use of bundlers it's not very likely |
14:07 | <Robert Pamely> | so bundled modules could be cached |
14:08 | <Robert Pamely> | security concerns aside |
14:09 | <Jack Works> | so bundled modules could be cached |
14:10 | <rbuckton> | if we allowed over the wire or on disk serialization of module expressions we'd need a formal binary AST |
14:11 | <Jack Works> | if we allowed over the wire or on disk serialization of module expressions we'd need a formal binary AST |
14:11 | <Robert Pamely> | Why is it any different than caching the react source from a CDN, if you cache the react source from a bundled site? |
14:11 | <Robert Pamely> | (I'm thinking out loud here) |
14:13 | <Kris Kowal> | I’m reasonably certain that module declarations would break new Module((module {}).source), which is key to user composition of module graphs. |
14:13 | <ryzokuken> | Right, but with module declarations you can now cache module based on some other identifier than URL |
14:14 | <bakkot> | Why is it any different than caching the react source from a CDN, if you cache the react source from a bundled site? |
14:14 | <danielrosenwasser> | I'm not huge on needing a separate namespace per module (or bit per value) to track which exports are module exports that can be imported from vs. not This blog post comes to mind https://gbracha.blogspot.com/2014/09/a-domain-of-shadows.html |
14:16 | <Kris Kowal> | Module declarations/expressions won’t compose because a module graph that consists of declarations coming from different module files cannot be consolidated into a single module handler, for the purposes of resolving stringly named module specifiers on their various referrers. |
14:16 | <rbuckton> | Curious about static importability and portability of import module foo from ... and module bar { import foo; } . Is that valid? |
14:17 | <Robert Pamely> | If I'm reading this correctly, the concern was that CDN sources were manipulated to add tracking code in them that got loaded into every site that uses them? |
14:17 | <leobalter> | aren't module declarations top level only? |
14:17 | <caridy> | Slides: https://docs.google.com/presentation/d/183cYPeUjhzVRuZ0O-gwXqp6KtFYxxhgYodbr7XNo_9o/edit |
14:18 | <rbuckton> | And what about:
There is so much inconsistency with the "two worlds" approach that breaks the decl/expr equivalence of function and class |
14:18 | <nicolo-ribaudo> | aren't module declarations top level only? The proposal as it is now allows you to do something like this:
|
14:19 | <leobalter> | thanks for the info |
14:19 | <rbuckton> | That's specific to just that case, not a general solution. |
14:19 | <Kris Kowal> | My other concern is that differentiating lexical and stringy module specifiers will be used to distinguish which modules should be serialized from which should be resolved from a remote worker’s import hooks, and will have ramifications on whether instances are shared. That’ll herd the module ecosystem in a weird direction, where portable libraries will generally have to export a module expression/declaration in order to give the user the choice of whether to link lazily or eagerly. |
14:19 | <nicolo-ribaudo> |
const x you had a let x , that is later reassigned? |
14:19 | <rbuckton> | You could be importing a module dynamically from another module that exports it, but can't reference it in another module expression. |
14:19 | <bakkot> | Robert Pamely: no, the concern is, my site can tell you've visited another site by timing whether loading a resource which is also loaded by that site is cache-hit or not |
14:19 | <bakkot> | https://github.com/shivanigithub/http-cache-partitioning has a better explainer I think |
14:19 | <Luca Casonato> | I think it may be a reasonable to investigate using a seperate identifier namespace to clarify the difference between "regular values" and "module values". For example module $foo {}; import $foo; |
14:20 | <Jack Works> | I think it may be a reasonable to investigate using a seperate identifier namespace to clarify the difference between "regular values" and "module values". For example import($foo) |
14:20 | <Jack Works> | so there is no need to separate a new namespace |
14:20 | <nicolo-ribaudo> | Curious about static importability and portability of Module object it can be easily made to work. |
14:21 | <Jack Works> | we can think of, some values are just available eariler, before the execute |
14:21 | <rbuckton> |
|
14:21 | <Luca Casonato> | we can think of, some values are just available eariler, before the execute |
14:22 | <Jack Works> |
|
14:22 | <Luca Casonato> | it's difficult to explain why this is the case:
|
14:22 | <rbuckton> | My point is that the "2nd world" approach makes module decls and module expressions inconsistent. |
14:22 | <rbuckton> | That's a refactoring hazard. |
14:23 | <leobalter> | Module expressions and declarations are among the most important changes coming to ECMAScript that positively affects web developers and code bundling in order to encourage a better usage of ES modules. I hope TC39 can dedicate more time for those topics in order to advance them without taking years. |
14:23 | <Jack Works> |
const ident = module {} . no more complex |
14:23 | <nicolo-ribaudo> | Thanks for the example, the only way it could work with this proposal is something like this (!!! semantics are different, but the intention is the same):
It's worth investigating when modules should "capture" available modules from the other scope, but it would still only allow capturing other modules and not generic values. |
14:23 | <Luca Casonato> | so maybe we can also accept |
14:24 | <rbuckton> | I think if we have two things called module where there is a declaration and an expression version, then what they contain and how they handle scoping should be consistent. |
14:25 | <Mathieu Hofman> | shu: currently the message and name access is specced to be non-observable, which mimics FF, but if there's an argument for otherwise, we could spec to always perform a Get |
14:25 | <nicolo-ribaudo> | I think if we have two things called |
14:25 | <Jack Works> |
Some thought: Link of module B in your example can be deferred to when |
14:27 | <rbuckton> | Well, your examples are about scoping of module declarations and const declarations |
14:27 | <Michael Ficarra> | uhhh censoring error stacks is already a proposal |
14:30 | <ryzokuken> | bakkot: user land -> userland |
14:31 | <rbuckton> |
|
14:34 | <rbuckton> | Accessing a serializable or replaceable binding in a module would be fine, but other bindings would not. There's a circularity issue there though:
This seems like a perfectly reasonable case, but you can't eagerly serialize |
14:36 | <danielrosenwasser> | a and b aren't capturable in that example though, right? |
14:37 | <rbuckton> |
|
14:38 | <Jack Works> |
b and a will be looked up in the global object of it's evaluator instead of links to the module |
14:38 | <rbuckton> | It would be extremely inconsistent if you cannot model with module expressions the same thing you can model with module declarations. This seems like a major stumbling block for adopters. |
14:38 | <danielrosenwasser> | oh, because they would be valid if it was written as
|
14:38 | <apaprocki> | Justin Ridgewell: they don't want if (runningInShadowRealm) |
14:38 | <danielrosenwasser> | wait...is that true though? |
14:39 | <rbuckton> | If it isn't then we don't have the same kind of decl/expr parity that function and class have. |
14:39 | <nicolo-ribaudo> |
|
14:39 | <rbuckton> | I'd almost prefer just having module declarations over having both, if only to prevent potential confusion by adopters due to the inconsistency. |
14:39 | <nicolo-ribaudo> | If it isn't then we don't have the same kind of decl/expr parity that function and class have. let className = class { ... |
14:40 | <danielrosenwasser> | So if you're running a in a context where globalThis.b exists, then a captured b takes precedence? |
14:42 | <rbuckton> | Functions already don't have parity - they have different scoping rules. Only classes can be transparently replaced with |
14:42 | <Michael Ficarra> | we could just advance the error stacks proposal... |
14:43 | <Michael Ficarra> | this would be a very small change on top of that |
14:49 | <nicolo-ribaudo> | If you have this code:
the dynamic In this case:
I see that it would be confusing, since How would you feel if bindings of module declarations where not captured, but only statically importable?
Note: the current spec text throws the ReferenceError in this case, but just because it was easier to specify. We didn't have a reason to do one thing or the other, but this possible confusion seems to indicate what we should keep the error. |
14:51 | <Michael Ficarra> | this sounds like a topic for TG3... |
14:52 | <Michael Ficarra> | I'm not convinced by the hopelessness being expressed |
14:53 | <apaprocki> | Is this the only way that script can determine it's running in a shadow realm? |
14:53 | <apaprocki> | (currently known) |
14:53 | <Michael Ficarra> | I mean it's non-standard, there could be non-standard "amIRunningInAShadowReam" function |
14:57 | <nicolo-ribaudo> | (thanks btw danielrosenwasser and rbuckton for explaining your concerns with clear code examples!) |
14:59 | <danielrosenwasser> |
|
14:59 | <apaprocki> | I mean it's non-standard, there could be non-standard "amIRunningInAShadowReam" function |
15:02 | <Mathieu Hofman> | Here is the open issue about invariants: https://github.com/tc39/proposal-shadowrealm/issues/324 |
15:04 | <caridy> | we could just advance the error stacks proposal... |
15:05 | <bakkot> | i had enough in me to stay awake until 7, barely, but I am very close to falling asleep before we finish this item |
15:06 | <Michael Ficarra> | commiserations to everyone who ended up in the overflow |
16:49 | <rbuckton> | Could a chair help me by finishing the transfer of https://github.com/tc39-transfer/proposal-async-explicit-resource-management to the tc39 org to help with the async resource management split? |
19:42 | <rbuckton> | Could a chair help me by finishing the transfer of https://github.com/tc39-transfer/proposal-async-explicit-resource-management to the tc39 org to help with the async resource management split? |
20:00 | <ljharb> | rbuckton: done |
20:01 | <rbuckton> | Thanks! |
21:59 | <littledan> | I think if we had module declarations, I would prefer the "do not implicitly capture" behavior, but it feels like that defeats a lot of what would make them "special". I'm not sure if that's the right answer, but I found the current behavior confusing because my mental model was "modules capture nothing" Honestly it sounds like we're starting to get at, module fragments should be in a separate namespace. Neither variables nor strings, but a secret third thing. As an obviously unusable strawman, let's say they begin with
|
21:59 | <littledan> | this separate namespace could actually be usable in expression context (to get the Module object), just not the opposite: it would be impossible to make a runtime variable with the same name |
22:00 | <littledan> | so then we'd have a clear explanation: module { } closes over just the ##xyz values, and nothing else |
22:01 | <littledan> | remember that we previously rejected strings specifiers for a couple reasons:
|
22:03 | <Alex Vincent> | rbuckton: I do want to confer with you sometime in December (but not in the next few days) about the resource management, cancellation, and proxy revocation proposals. Do you attend SES's strategy sessions occasionally? |
22:24 | <ljharb> | oof - a parallel namespace gives me all sorts of bad feelings. the committee reacted pretty strongly against that with hax's extensions proposal too, iirc |
22:25 | <littledan> | well, we made a separate namespace for private names :) Arguably the specifiers already are a separate namespace, as well. I think the viability depends a lot on if we can come up with an agreeable syntax. |
22:26 | <littledan> | also it depends whether a proposal "pays for itself" (introducing a significant enough new capability) and meets developer expectations (For extensions, I think developers hope it will magically work with x.y() ) |
22:29 | <ljharb> | i would think dev expectations of module fragments/blocks is that they either capture everything, like a function, or nothing at all (in which case the way to "pass in" something would be like in php's using declaration on closures) |
22:32 | <littledan> | Yeah, another way around this is another top-level production/mimetype (like web bundles, but it has to be processable just by the JS engine). That introduces other issues, though. |
22:33 | <littledan> | i would think dev expectations of module fragments/blocks is that they either capture everything, like a function, or nothing at all (in which case the way to "pass in" something would be like in php's |
22:34 | <ljharb> | "would think" is particularly strong? |
22:34 | <littledan> | never mind |
22:35 | <ljharb> | i would also think that any of us that have extensive experience interacting with devs in general would indeed have some sense, immediately, of what might be suitable, even tho more research is always helpful |