00:14 | <ljharb> | reading through it, it kind of seems like the conclusion of this page is roughly identical (modulo placeholder choice, perhaps) to the JS proposal? |
01:42 | <littledan> | Huh, I assumed that the range operator| was supposed to subsume pipeline |
04:17 | <Ashley Claymore> |
|
04:17 | <Ashley Claymore> | Wild! |
04:19 | <bakkot> | honestly kinda like scala's _ |
04:32 | <rbuckton> | honestly kinda like scala's ? , though doing that with operators required other syntax. |
05:15 | <littledan> | Notes editors: let’s remember to remove the >> and add a blank line between different speakers (and text that you want to render in different paragraphs in general). I did this for some topics but I think we can also do it on-line as well. |
06:10 | <ljharb> | scala has something like 18 meanings for _ , it's very confusing until you've internalized all the relevant contexts |
08:50 | <pipobscure> | At least wikipedia article also use ID as the abbr for identifier or identity ( https://en.wikipedia.org/wiki/Identifier ) |
08:50 | <pipobscure> | So wikipedia is probably not a good source to go for here. |
09:27 | <Ashley Claymore> | _ ? _ : _ |
11:04 | <eemeli> | at any rate i feel very strongly against "Id" but "identifier" or "code" seem fine to me |
12:32 | <Ashley Claymore> | This should now be OK for day 1 & 2. |
12:33 | <Ashley Claymore> | I think we should also keep the practice of starting every paragraph with the acronym, even if it's a continuation of the current speaker. To make it super clear that it is a continuation and not a missing acronym. |
13:44 | <HE Shi-Jun> | What are you talking about? |
13:46 | <littledan> | I think we should also keep the practice of starting every paragraph with the acronym, even if it's a continuation of the current speaker. To make it super clear that it is a continuation and not a missing acronym. |
13:53 | <Anthony Bullard> | One point to note there, the transcriptionist introduces many unnecessary or unnatural newlines This affects the grammar checking a lot, and is very tedious to clean up(and sometimes to even notice) |
14:00 | <Ashley Claymore> | yep hopefully that can get sorted at some point. I would say "surely that is an easy part of the software to change" but we all know not to assume the ease of making code changes to software :D |
14:02 | <Ashley Claymore> | a post-meeting script that replaces newlines where the previous line has text which does not end in punctuation might cover the majority of the occurrences. |
14:03 | <littledan> | Yes, we complained about these new lines to the transcriptionist, and I will also follow up with the company after this meeting to see if there is any possible fix. |
14:05 | <pipobscure> | check the page history, someone changed ID to Id yesterday. 🫥 |
14:06 | <pipobscure> | Even years of interwebs experience still let me forget that tone doesn’t always translate well. /blush |
14:11 | <littledan> | Yeah I am pretty baffled, what does vandalizing Wikipedia have to do with what we are discussing? |
14:13 | <pipobscure> | Yeah I am pretty baffled, what does vandalizing Wikipedia have to do with what we are discussing? I resent that (vandalizing?). That was an entirely valid edit. Also you do have to explain why the change is an improvement. So… But it does go to show that some sources (i.e. wikipedia) can’t really be valid precedent in this argument. |
14:15 | <littledan> | OK, I think we all know Wikipedia can be edited… not sure what that proves |
14:16 | <littledan> | The bigger point here is, we should be able to collectively bikeshed something and be able to draw a conclusion based on treating all delegates as equal and respecting all the points raised. This is what we are having trouble doing here. |
14:21 | <littledan> | I think our unanimity model is getting a little frustrating here because folks feel like threats of blocks are being used in a way that makes other delegates feel like their point will be ignored (which would violate the original goal of our consensus-seeking in the first place). This equality/inclusion/process question is a bigger issue than the actual contents of the arguments (since anything will work technically). |
14:51 | <Anthony Bullard> | Luckily we could ensure that doesn't happen. Scala evolves with a very different process |
14:51 | <Anthony Bullard> | And values different things |
14:52 | <Anthony Bullard> | But as someone who wrote it in some capacity professionally for four years, the _ seems like a more natural placeholder |
14:55 | <Anthony Bullard> | Compiled languages have it so much easier in many ways... |
15:00 | <msaboff> | I think our unanimity model is getting a little frustrating here because folks feel like threats of blocks are being used in a way that makes other delegates feel like their point will be ignored (which would violate the original goal of our consensus-seeking in the first place). This equality/inclusion/process question is a bigger issue than the actual contents of the arguments (since anything will work technically). |
15:01 | <msaboff> | I think all other TC's use a true consensus model. |
15:10 | <Anthony Bullard> | It feels like there needs to be a very clear model of what kind of dissent and by how many parties constitutes a breakage of consensus. |
15:14 | <bakkot> | re: async contexts, I am assuming that the behavior is, whenever a function is scheduled to be run on a subsequent microtask tick or turn of the event loop, the host is required to snapshot the current async context bits and then restore them before running the function? |
15:14 | <HE Shi-Jun> | If not one, how many? To be honest, I feel the problem is not 100% or 80%. |
15:14 | <bakkot> | i.e. this isn't just for literal async but also for stuff like addEventListener ? |
15:14 | <yulia> | I would support the change to a consensus model that doesn't require 100% agreement. I don't like that one veto stops something. |
15:15 | <bakkot> | though event listeners are kind of an odd case, because they run sync if you dispatch an event manually, so I'm not totally clear what's supposed to happen there... |
15:15 | <Ben Newman (Apollo, @benjamn on GH)> | re: async contexts, I am assuming that the behavior is, whenever a function is scheduled to be run on a subsequent microtask tick or turn of the event loop, the host is required to snapshot the current async context bits and then restore them before running the function? setTimeout ) use something similar to AsyncContext.wrap for their callbacks |
15:15 | <shu> | bakkot: that's been my understanding as well |
15:15 | <shu> | i'm kind of confused by justin's example saying the propagation point is at the await pause |
15:15 | <ljharb> | there is a decent list of proposals that were blocked by a lone objector, including some of the folks discussing changing the process here, fwiw. would we revist all of those if there were a process change? |
15:15 | <shu> | it can't work if it's just promise reactions |
15:16 | <Ben Newman (Apollo, @benjamn on GH)> | right, but those are the hardest to polyfill |
15:16 | <bakkot> | i'm kind of confused by justin's example saying the propagation point is at the await pause |
15:16 | <shu> | bakkot: that's the second one, i thought there was a handler scheduled earlier |
15:16 | <shu> | i might be mixing slides? |
15:17 | <shu> | anyway my point is just the host hooks i added for incumbent are only for JS callbacks |
15:17 | <shu> | this needs host event loop hook-ins as well |
15:18 | <bakkot> | indeed, as this slide is saying |
15:19 | <bakkot> | I will ask the question about event listener dispatch I guess |
15:19 | <Ben Newman (Apollo, @benjamn on GH)> | are folks here aware of the V8 methods v8::Context::{Get,Set}ContinuationPreservedEmbedderData ? |
15:19 | <shu> | that's not quite enough |
15:19 | <shu> | we have that, yes, but you need more muxing |
15:19 | <Ben Newman (Apollo, @benjamn on GH)> | I know, but I'm trying to gauge context |
15:20 | <Ashley Claymore> | I will ask the question about event listener dispatch I guess |
15:21 | <bakkot> | Ashley Claymore: the event listener question is less "how is this implemented" and more "what is the thing you want here" - I'm trying to get at the mental model, not the implementation |
15:21 | <littledan> | I disagree with Justin's example: i think web platform built-ins should do the wrap for you. It's more like if you have JS code which queues callbacks, that you want wrap . |
15:22 | <Ben Newman (Apollo, @benjamn on GH)> | that becomes a lot easier if/once AsyncWrap is standardized |
15:22 | <Chengzhong Wu> | Ashley Claymore: the event listener question is less "how is this implemented" and more "what is the thing you want here" - I'm trying to get at the mental model, not the implementation |
15:22 | <littledan> | that becomes a lot easier if/once |
15:22 | <bakkot> | Chengzhong Wu: right, and others are emitted synchronously (e.g. by a manual dispatchEvent ) |
15:24 | <bakkot> | so: if you register an event listener while within an async context, and then an event is dispatched async by the browser, presumably you want the listener to see the original context. but what if the event is dispatched sync, by dispatchEvent ? sync dispatch is basically just a weird sort of function call, so it seems like it's the current context, not the original one? |
15:24 | <bakkot> | not clear to me which context you want there |
15:25 | <littledan> | Unfortunately the answer might depend on the event |
15:25 | <Ben Newman (Apollo, @benjamn on GH)> | sync function calls should just inherit the current context, leading to the same behavior? (pls clarify) |
15:25 | <littledan> | and there may be up to three answers for different task types |
15:25 | <littledan> | this would be part of the DOM/HTML/Node.js integration with AsyncContext |
15:25 | <Chengzhong Wu> | Yeah, that's the point of an alternative option captureAsyncContext of addEventListener |
15:25 | <bakkot> | sync function calls should just inherit the current context, leading to the same behavior? (pls clarify) |
15:26 | <littledan> | Yeah, that's the point of an alternative option |
15:26 | <Ben Newman (Apollo, @benjamn on GH)> | the event should be dispatched with the context you bound/wrapped, whether that happens synchronously or async |
15:26 | <littledan> | the event should be dispatched with the context you bound/wrapped, whether that happens synchronously or async |
15:26 | <littledan> | I think we have to look event by event to figure this out |
15:27 | <Ben Newman (Apollo, @benjamn on GH)> | sorry I think we must be talking past each other or about slightly different things |
15:27 | <bakkot> | the event should be dispatched with the context you bound/wrapped, whether that happens synchronously or async |
15:30 | <Andreu Botella> | calling a wrapped synchronous function would also let you change a context synchronously |
15:30 | <bakkot> | OK I think I understand now |
15:35 | <bakkot> | the fact that not all callbacks will necessarily automatically inherit the context in which the callback was added (depending on the API which adds the callback) is a little bit awkward, since the whole point of this is that you want to define a context which is available when your callback runs even though your callback is getting registered to run by code you don't control |
15:35 | <Michael Ficarra> | is there any precedent for an API like this? |
15:35 | <Michael Ficarra> | it's a stage 2 concern, but I'm very skeptical that this is the right representation |
15:38 | <Chengzhong Wu> | https://github.com/legendecas/proposal-async-context#prior-arts |
15:39 | <Chengzhong Wu> | These are javascript precedents. C# also implements similar APIs |
15:39 | <Ashley Claymore> | Reactivity libraries with dependency tracking like SolidJS also have similar though more domain specific APIs |
15:39 | <Chengzhong Wu> | https://learn.microsoft.com/en-us/dotnet/api/system.threading.asynclocal-1?view=net-7.0 |
15:41 | <bakkot> | here's the link from Mark from the jitsi chat: https://github.com/endojs/endo/blob/markm-fluid-scopes/packages/eventual-send/src/async-contexts/6-async-context-transpose.js |
15:42 | <Michael Ficarra> | thanks for the links, Chengzhong Wu and Ashley Claymore |
15:42 | <bakkot> | the fact that not all callbacks will necessarily automatically inherit the context in which the callback was added (depending on the API which adds the callback) is a little bit awkward, since the whole point of this is that you want to define a context which is available when your callback runs even though your callback is getting registered to run by code you don't control .wrap the function first |
15:42 | <littledan> | I have no idea how a JS engine would implement that super fancy context splitting that Shu was alluding to |
15:42 | <littledan> | this is a bit distinct from weak maps/references |
15:43 | <littledan> | always impressed by all JS engines' optimization ideas |
15:44 | <Ashley Claymore> | Someone might be able to get a published paper out of optimizing this :D |
15:45 | <Ashley Claymore> | "This one weird trick to get zero overhead async contexts" |
15:46 | <shu> | v8 has zero overhead async contexts |
15:46 | <bakkot> | thinking about this even more, I am tempted to say that automatic wrapping happens only when you can't do it yourself, i.e. only for syntactic yield and await (and friends, like using await , of course) - in every other case (including manual calls to .then ) you can just wrap the continuation yourself, and not have to worry about whether the scheduler you're using happens to do wrapping for you |
15:46 | <shu> | the trick is that sometimes you can't get the stack! |
15:47 | <Andreu Botella> | https://github.com/wintercg/proposal-common-minimum-api/blob/main/asynclocalstorage.md |
15:48 | <rbuckton> | thinking about this even more, I am tempted to say that automatic wrapping happens only when you can't do it yourself, i.e. only for syntactic .then . The biggest problem with context passing and Promises is that you would need to potentially wrap every .then callback in the event one of them throws. This is far more arduous than wrapping the single entrypoint of an event handler. |
15:48 | <rbuckton> | If it works with await , it should work with .then on native Promise implementations. |
15:49 | <bakkot> | I mostly agree, with the exception of .then calls, which isn't so common, and even then if you factor out a helper to do the wrapping it seems not so arduous? |
15:49 | <bakkot> | but I am maybe open to it; something to talk about more |
15:50 | <rbuckton> | I disagree. If you are still using .then today when async/await exists, you are likely doing something far more complex than a single .then continuation. |
15:50 | <ljharb> | tons of people do that with fetch - await fetch(…).then(x => x.json()) |
15:50 | <ljharb> | it's pretty common |
15:51 | <bakkot> | I am OK with the people doing the fetch thing having to refactor their code or do a manual wrap |
15:51 | <bakkot> | but yes sometimes things get more complicated |
15:52 | <ljharb> | i haven't been following, but it seems pretty important to preserve the sugar-ness of await over promises. |
15:52 | <bakkot> | my main concern with automatically wrapping in .then is, the rule "you have to wrap any callback, whereas syntactic continuations get wrapped for you" is much easier to learn than "Promise.then automatically wraps, but [some other subset of schedulers] does not" |
15:53 | <bakkot> | i haven't been following, but it seems pretty important to preserve the sugar-ness of await over promises. |
15:53 | <Ashley Claymore> | in the .json case, it will probably be fine that it doesn't keep the context, unless .json has been patched |
15:53 | <Michael Ficarra> | oohh I think I like that WinterCG proposed API better |
15:53 | <ljharb> | that's fine as long as the non-await usage isn't made more complicated - altho i guess it might be in order to use the new functionality without the sugar, as is the case with class |
15:53 | <littledan> | Can someone link to the YouTube playlist? |
15:53 | <littledan> | for the minutes |
15:54 | <rbuckton> | It will also be difficult to explain why await x; doSomethingWithContext() would work, but x.then(() => doSomethingWithContext()) wouldn't. Especially when many editors have refactorings for that, which now become potentially unsafe operations. |
15:55 | <bakkot> | It will also be difficult to explain why |
15:55 | <bakkot> | and if you're not using an async context in your own function body you don't need to learn anything |
15:55 | <Ashley Claymore> | Can someone link to the YouTube playlist? |
15:56 | <yulia> | I appreciate the investigation into performance from v8 and the security review from SES. From our side, we see use cases for this but need to do a more thorough review. No outstanding concerns from us right now |
15:56 | <shu> | what is mark talking about? |
15:56 | <shu> | is this the AsyncContext spec draft or 262? |
15:58 | <bakkot> | the other side of this, it will be difficult to explain why x.then(doSomethingWithContext) would work, but setTimeout(doSomethingWithContext) would not. so that implies setTimeout needs to automatically wrap as well. and then now you have the same problem but with s/setTimeout/setEventListener/ , and so on |
15:58 | <rbuckton> | This becomes one more thing to consider when wrapping code in an AsyncContext , especially if that code calls into a third-party package that might use .then . |
15:58 | <Michael Ficarra> | since this is only stage 1, can we word the proposal (in its repo and the proposals list) in terms that don't lock us into any API decisions? something like "hooks for associating state with async call stack frames"? |
15:59 | <rbuckton> | the other side of this, it will be difficult to explain why setTimeout , setImmediate , process.nextTick were discussed in the slides as other potential places to automatically flow state. |
15:59 | <bakkot> | This becomes one more thing to consider when wrapping code in an .then , like event listener dispatch or something. |
15:59 | <Michael Ficarra> | Justin Ridgewell: ^ |
16:00 | <bakkot> | i.e., if you're passing a callback to another library, you need to wrap it anyway, because you can't trust that the library is going to do the scheduling using one of the automatically-wrapping APIs (or even schedule it at all - some libraries do deliberately invoke zalgo) |
16:00 | <bakkot> | so making the rule "you always need to wrap callbacks" is much simpler |
16:02 | <rbuckton> | so making the rule "you always need to wrap callbacks" is much simpler |
16:02 | <bakkot> | if you are passing a callback to another library, you don't know that it's categorically an async continuation. |
16:03 | <bakkot> | it's just a callback. whether it's an async continuation depends on how that library consumes the callback. |
16:03 | <bakkot> | so if you want to preserve the async context, you need to mark it as an async continuation, by wrapping it |
16:03 | <rbuckton> | Simpler is knowing that asyncIterable[Symbol.asyncIterator]().map(element => { /*has outer context*/ }) will work, but since there's no explicit await , how would I know? |
16:06 | <bakkot> | Regardless of whether .then automatically wraps, I could write a Symbol.asyncIterator implementation that does not in fact wrap the callback using the top-of-stack callback; that's up to the implementation of Symbol.asyncIterator . |
16:06 | <rbuckton> | I also think not supporting this in .then would make it far less useful to quite a few of the use cases this is intended to support. |
16:06 | <rbuckton> | I'm pointing at the async iterator helpers proposal's built-in capabilities here. |
16:07 | <bakkot> | Right, but my point is that the async iterable is something someone hands you, and you can't trust that it's only going to use the scheduling APIs you expect it to on the way to its ultimate call to Promise.prototype.then . |
16:08 | <bakkot> | And the whole point of this API is that you shouldn't have to coordinate with other people's code about how they choose to schedule your callback. If you could coordinate, you could just have the other code thread the state through. |
16:09 | <littledan> | There was some fascinating investigation into a sort of "borrowing" model for ArrayBuffers in https://gist.github.com/domenic/a9343fa787ba54b4ba3a60882c49cc32#file-readme-md |
16:09 | <bakkot> | Which is why I think we should not encourage you to write patterns which rely on other people's code using particular APIs to do scheduling. |
16:10 | <bakkot> | i.e., we should not make some APIs do wrapping of callbacks automatically. |
16:10 | <littledan> | This is a different primitive; it couldn't be implemented in transfer . It'd be better IMO. Anyway, done is better than perfect, we already had Stage 3 on this current thread |
16:10 | <littledan> | i.e., we should not make some APIs do wrapping of callbacks automatically. |
16:11 | <bakkot> | littledan: nope |
16:11 | <littledan> | well... I think the implicit wrapping is just a sort of requirement to make any of this work in practice. |
16:11 | <Chengzhong Wu> | It's still stated as possible solution in the readme. |
16:12 | <bakkot> | implicit wrapping of syntactic await /yield continuations, absolutely |
16:12 | <bakkot> | implicit wrapping of explicit callbacks, I don't see it. you can wrap those yourself. |
16:13 | <littledan> | I think it'd be worth looking at existing use cases to see whether your suggestion would be viable... |
16:13 | <bakkot> | indeed you have to wrap those yourself if the callback is going to thread through someone else's code, because you can't trust that they're going to do the scheduling using one of those automatically wrapping APIs |
16:13 | <littledan> | I mean, cases, where this is already deployed |
16:14 | <Justin Ridgewell> | yield is something we'll need to discuss: https://github.com/legendecas/proposal-async-context/issues/18 |
16:14 | <Justin Ridgewell> | For await and Promise.p.then , I think we have to do them automatically |
16:14 | <Justin Ridgewell> | await should just be a sytnax on top of promises |
16:14 | <Justin Ridgewell> | (And also if you force me to take away the fast path for await promise because I have to patch .then , I will riot) |
16:14 | <littledan> | I think bakkot's arguments would need to be made to host environments, since we just don't own much of what would need to make these decisions |
16:15 | <bakkot> | (And also if you force me to take away the fast path for .then |
16:15 | <Justin Ridgewell> | As the platform, I do |
16:15 | <bakkot> | ... why? |
16:15 | <bakkot> | Assuming there's implicit wrapping in syntactic await s |
16:15 | <Justin Ridgewell> | The whole console.log use case in the slides |
16:15 | <littledan> | Assuming there's implicit wrapping in syntactic |
16:15 | <Justin Ridgewell> | I have to preserve across any promise usage, because anything else will break an important API for us |
16:16 | <littledan> | what I don't understand is the motivation |
16:16 | <littledan> | I have to preserve across any promise usage, because anything else will break an important API for us |
16:16 | <bakkot> | I have to preserve across any promise usage, because anything else will break an important API for us |
16:16 | <Justin Ridgewell> | I think he's saying I don't |
16:16 | <littledan> | by "you" I mean, either the program or the Vercel environment |
16:17 | <Justin Ridgewell> | Vercel's platform |
16:17 | <Justin Ridgewell> | https://docs.google.com/presentation/d/1yw4d0ca6v2Z2Vmrnac9E9XJFlC872LDQ4GFR17QdRzk/edit#slide=id.g18e6eaa50e1_0_164 |
16:17 | <Justin Ridgewell> | I have to preserve this start time across any user code per request |
16:17 | <littledan> | Let's slow down; I think you're talking past each other |
16:17 | <Justin Ridgewell> | It doesn't matter if they use setTimeout , p.then() , or await , it's all got to work |
16:18 | <bakkot> | Justin Ridgewell: right, and that would still work? |
16:18 | <bakkot> | I am confused |
16:18 | <bakkot> | You need to wrap your handlers before you hand them to other people |
16:18 | <Justin Ridgewell> | How? If I force my users to wrap their p.then() callbacks, then it's not seamlessly working |
16:18 | <bakkot> | and as long as you do that, the handlers run in your context, regardless of how other people are scheduling them to run |
16:19 | <Justin Ridgewell> | console.log isn't a callback I pass around, it exists in the global scope |
16:19 | <Justin Ridgewell> | I can't wrap it per requests, because the global value would change per request |
16:20 | <Justin Ridgewell> | They don't store an access to the current value, they just access the property on the shared console object |
16:20 | <rbuckton> | You need to wrap your handlers before you hand them to other people |
16:21 | <bakkot> | Justin Ridgewell: oh, sorry, I missed that it is crucial to this example that you are patching global state and expecting that to be respected. I am not convinced that's something worth designing for. |
16:21 | <bakkot> | If you automatically associate context for .then, you are saving users a significant amount of complexity and boilerplate. If you are calling into questionable code, the presence of automatic context copying doesn't prevent you from manually wrapping the callback yourself. |
16:22 | <bakkot> | if you aren't calling into other people's code, you don't need async contexts at all. and if you are, you need to wrap. I don't see how any work is saved here. |
16:23 | <Ashley Claymore> | Other use cases are distributed trace, where request handlers are, eventually, resolved by combing other requests. That's not 'global state' like console.log , but still a global singleton to make a request |
16:24 | <Justin Ridgewell> |
This would be a significant change from the way Node works, and would be a huge burden |
16:24 | <Justin Ridgewell> | And it's also going to force me out of my promise fast path |
16:24 | <Justin Ridgewell> | My two proposals won't play nice with each other |
16:24 | <bakkot> | I can't wrap it per requests, because the global value would change per request addEventListener doesn't automatically wrap callbacks passed to it, how does this work if the dev code, instead of using await , had added an event listener which would do the console.log at some later time? |
16:24 | <Michael Ficarra> | I think transferAndFixLength is better than transferToFixedLength |
16:24 | <Justin Ridgewell> | I think it has to work for promises |
16:25 | <ljharb> | I think |
16:25 | <littledan> | "calling into questionable code" meaning calling into... any code? |
16:25 | <Justin Ridgewell> | Devs cannot escape our context on our platform, so any future point in which they would trigger an event listener would already have restored the request context |
16:26 | <Michael Ficarra> | the same overload as Fixed , no? |
16:26 | <bakkot> | littledan: while true, I also believe that a refactoring from scheduling with promises to scheduling with some other mechanism should be transparent to consumers of your code. |
16:26 | <littledan> | yeah, Bloomberg's use case also has requirements that we strictly not allow "escape". |
16:26 | <bakkot> | that is, we should not design the language such that such a refactoring is by-default not transparent. |
16:26 | <littledan> | In APMs OTOH, they would simply not work if everyone had to update all of their code... they are just based around implicit wrapping |
16:27 | <littledan> | like, this was proven out historically, leading to its development |
16:27 | <ryzokuken> | APMs have used decorators in the past to get around that though |
16:28 | <Michael Ficarra> | shu: please try to give a little more time between a name change and a stage 3 advancement next time; we may have been able to come up with a better name if we had more time |
16:28 | <ryzokuken> | well, although that'd still need changes |
16:28 | <bakkot> | Devs cannot escape our context on our platform, so any future point in which they would trigger an event listener would already have restored the request context |
16:28 | <littledan> | shu: please try to give a little more time between a name change and a stage 3 advancement next time; we may have been able to come up with a better name if we had more time |
16:28 | <bakkot> | but that does lock us into, every way of scheduling a task in the host must automatically wrap the callback passed to that context |
16:28 | <shu> | Michael Ficarra: technically there wasn't a name change |
16:28 | <Michael Ficarra> | littledan: because it didn't matter enough |
16:28 | <bakkot> | eventListener, unhandled promise handler, everything |
16:29 | <Chengzhong Wu> | you can AsyncContext.wrap the callback in such case |
16:29 | <bakkot> | Chengzhong Wu: well, that's my argument |
16:29 | <bakkot> | if some ways of scheduling require you to wrap, they all should |
16:29 | <bakkot> | but if no ways of scheduling require you to wrap, that's fine too |
16:30 | <Justin Ridgewell> | Promises are a way for our users to later schedule a task, we do not control where they create promises or where the chain .then() |
16:30 | <shu> | Michael Ficarra: transfer was always named transfer, but because of the "preserve resizability" semantics change, to get the original "fixed length" behavior, i added a new method |
16:30 | <bakkot> | I am just not enthusiastic about some callback-taking scheduling APIs wrapping |
16:30 | <Chengzhong Wu> | But the default behavior can be that people can get the host defined context when using host apis. |
16:30 | <shu> | this meeting is the first meeting i presented the new method |
16:30 | <Justin Ridgewell> | So in order for us to guarantee they cannot escape our context, the promise.then() must preserve context |
16:30 | <Michael Ficarra> | littledan: I don't think we need to introduce a formal process around it. If I think the name was bad I would have blocked advancement. |
16:30 | <littledan> | littledan: because it didn't matter enough |
16:30 | <Justin Ridgewell> | Things that obviously schedule an async task should preserve async context |
16:30 | <Michael Ficarra> | shu: true, but I had reviewed the proposal with the earlier name |
16:31 | <bakkot> | So in order for us to guarantee they cannot escape our context, the |
16:31 | <shu> | Michael Ficarra: yeah also fair, it was kind of last minute there |
16:31 | <bakkot> | which I am fine with, if it's viable |
16:31 | <littledan> | Michael, this is exactly what we introduced the "note dissent that's non-blocking" part for |
16:31 | <Ashley Claymore> | Note: Summary needs writing for previous item |
16:31 | <Justin Ridgewell> | yes, and also every other possible way of scheduling a callback |
16:31 | <littledan> | Michael, this is exactly what we introduced the "note dissent that's non-blocking" part for |
16:31 | <Justin Ridgewell> | Key word is scheduling here |
16:31 | <bakkot> | "addEventListener" counts as scheduling, of course |
16:32 | <bakkot> | and onMessage |
16:32 | <Justin Ridgewell> | It doesn't |
16:32 | <Justin Ridgewell> | There's not obvious point where the callback would be invoked |
16:32 | <bakkot> | ok then I don't understand what you're saying |
16:32 | <bakkot> | I can make a thing which looks exactly like a promise but which uses addEventListener to schedule the callback to run |
16:32 | <Michael Ficarra> | so I'm wondering what's not going to work about that |
16:32 | <Justin Ridgewell> | await schedules a task to happen as soon as the promise settles (same for .then() ), setTimeout schedules to run after X ms, etc |
16:32 | <bakkot> | there is no fundamental difference btween these things |
16:33 | <Justin Ridgewell> | There is |
16:33 | <Justin Ridgewell> | It exists only in imperative user code, not as part of the language |
16:33 | <bakkot> | What is "it" in that sentence? |
16:34 | <Justin Ridgewell> | Whatever calls your registered listener |
16:34 | <bakkot> | "this callback gets called when the user clicks a button" is exactly the same kind of thing as "this callback gets called when the network request completes [and therefore settles the fetch promise]" |
16:35 | <bakkot> | like, XMLHttpRequest 's onload is not a different kind of thing from fetch 's .then |
16:35 | <Justin Ridgewell> | That invocation isn't done automatically per the scheduling |
16:36 | <Justin Ridgewell> | Node has a long history of how this works, and I'm really hesitant to break with their precedent |
16:36 | <bakkot> | If there is a meaningful difference between XMLHttpRequest 's onload and fetch 's .then , I am not yet seeing it |
16:37 | <bakkot> | and in the absence of such a difference, I do not think those should behave differently wrt AsyncContext |
16:37 | <bakkot> | I am OK with both "both of those APIs wrap" and "neither API wraps", but not only one of them doing so |
16:38 | <Justin Ridgewell> | The difference is the promise instance? |
16:38 | <rbuckton> | I could see the point that setImmediate , setTimeout , setInterval , and even process.nextTick not automatically flow context, but AsyncContext is an async control-flow capability. await and then are explicitly async control-flow operations. The Timers API's aren't precisely async control flow, though async control flow has been implemented on top of them in the past. |
16:38 | <Justin Ridgewell> | The fact that it exists in ecma262 |
16:39 | <Justin Ridgewell> | The internal event listener that fetch uses doesn't need extra work, because the promise it returns is responsible for scheduling continuations |
16:39 | <bakkot> | Why are promise instances relevant? |
16:39 | <rbuckton> | I'm fairly certain AsyncContext must at least support both await and .then to be truly viable. |
16:40 | <bakkot> | Why is "it exists in ecma262" relevant, or even something you could reasonably ask a developer to know? |
16:40 | <rbuckton> | Why are promise instances relevant? |
16:40 | <bakkot> | More to the point: Justin Ridgewell , you said that for your abstraction to work, there must not be a way to escape the context. if onload is a way to escape the context, then... ??? |
16:40 | <Justin Ridgewell> | That's a great way to put it |
16:40 | <bakkot> | so are callbacks! |
16:41 | <bakkot> | "Promises are sugar for callbacks" is at least as important as "await is sugar for Promises" |
16:41 | <rbuckton> | Callbacks aren't explicitly async control flow. |
16:41 | <rbuckton> | "Promises are sugar for callbacks" is at least as important as "await is sugar for Promises" |
16:42 | <bakkot> | Sure, sugar for CPS, then. But onload is CPS also. |
16:42 | <rbuckton> | No, onload is reactivity. It isn't expressly intended to solve async control-flow. |
16:43 | <bakkot> | I do not understand the distinction you're drawing here. |
16:43 | <rbuckton> | Promise is expressly intended to solve async control flow. |
16:44 | <bakkot> | backing up a step from whether something is or is not "explicitly" async control flow, my position is that it is not reasonable to ask developers to intuit that XHR's .onload is a fundamentally different kind of thing than fetch 's .then , especially since we've spent most of the last decade going around telling everyone that the latter is sugar for the former. |
16:44 | <bakkot> | so I am not OK with designs which require developers to intuit this difference. |
16:45 | <ptomato> | ljharb: I'd like to try to have a call to resolve the Id issue, are you available during the break today? |
16:45 | <bakkot> | but I am OK with both possible designs which treat those as the same kind of thing - making onload wrap, or making then not wrap. |
16:45 | <rbuckton> | I think your definition of CPS is is far more broad than what is generally accepted. |
16:46 | <bakkot> | again, I am not super committed to the definition of terms here. |
16:46 | <ljharb> | ljharb: I'd like to try to have a call to resolve the |
16:46 | <bakkot> | the thing which is important to me is the bit about whether or not developer should know to treat .onload as a different kind of thing from .then |
16:46 | <bakkot> | and, specifically, whether developers who aren't doing anything with async contexts at all should have to know that |
16:47 | <rbuckton> | I think that whether onload should wrap should be up to WHATWG. TC39 doesn't spec onload , however. What we do specify is Promise . |
16:47 | <ptomato> | absolutely, thanks |
16:47 | <bakkot> | refactoring my library from fetch to XHR should not break consumers of my library just because they happened to be using async contexts. |
16:48 | <ljharb> | yup, just shoot me a link, i'm open |
16:48 | <rbuckton> | That would already have consequences |
16:50 | <rbuckton> | For example, you would have to manually handle error propagation. If not, then there are observable differences in whether that error is a rejection, an unhandled promise rejection, or some other host-defined error-handling mechanism. |
16:51 | <rbuckton> | By using Promise , you get a lot of async control flow functionality for free. Automatically flowing an AsyncContext across async control flow is more free functionality. |
16:51 | <bakkot> | OK, so assume I am manually handling error propagation. |
16:52 | <rbuckton> | You, as the library author, could also manually AsyncContext.wrap the incoming callback to support context flow. |
16:52 | <bakkot> | As the library implementor, I should not have to care about whether or not I am flowing an AsyncContext . That is, as I understand it, literally the entire point of this feature. |
16:52 | <ptomato> | I've created https://meet.jit.si/identifier. not all the champions will be there but Justin, Richard, and I will |
16:54 | <littledan> | Please join the fun AsyncContext discussion at #tc39-async-context:matrix.org ! |
16:55 | <rbuckton> | As the library implementor, I should not have to care about whether or not I am flowing an await , Promise , AsyncContext ), then yes it should be transparent. As a library author you should also be able to explicitly escape the current context, which is also achievable with AsyncContext.wrap . |
16:57 | <rbuckton> | In fact, if you were wrapping your XHR onload in a new Promise(...) , having .then do async control flow for you would be a win, because you still get AsyncContext flow for free. |
16:57 | <shu> | littledan: MatrixError: [403] You are not invited to this room. (https://matrix-client.matrix.org/_matrix/client/r0/join/%23tc39-async-context%3Amatrix.org) 🥺 |
16:57 | <bakkot> | The thing that Justin Ridgewell said earlier was that they are trying to "guarantee they cannot escape our context" |
16:57 | <bakkot> | which means that "some subset of things will preserve the context transparently" is not good enough |
17:00 | <Ashley Claymore> | I think it will always be possible for a library to run a callback in a pre-captured context? but the user can always wrap their callback to force it back. |
17:00 | <rbuckton> | which means that "some subset of things will preserve the context transparently" is not good enough await and Promise must support automatic flow at a minimum to be viable. |
17:01 | <Mathieu Hofman> | It means any API or syntactic surface the user code has available to them cannot allow them to escape an AsyncContext that was created when invoking the user code the first time |
17:01 | <bakkot> | I am still confused about what you mean by "viable", since, while I grant that those are necessary for the thing Justing wants, they are certainly not sufficient |
17:01 | <Mathieu Hofman> | built-ins like Promise and syntax like await must preserve this |
17:01 | <bakkot> | by "viable" do you mean something like "convenient"? |
17:01 | <Justin Ridgewell> | Please join the fun AsyncContext discussion at #tc39-async-context:matrix.org ! |
17:01 | <littledan> | littledan: MatrixError: [403] You are not invited to this room. (https://matrix-client.matrix.org/_matrix/client/r0/join/%23tc39-async-context%3Amatrix.org) 🥺 |
17:02 | <littledan> | I think we need to transfer it to the TC39 space first, then open up registrations to any space members |
17:02 | <Mathieu Hofman> | the only other feature in the language async calling is FinalizationRegistry callbacks, which should also capture at creation |
17:02 | <bakkot> | built-ins like Promise and syntax like await must preserve this |
17:02 | <Mathieu Hofman> | anything else is a host API, that can be virtualized by the environment executing the user code |
17:03 | <Mathieu Hofman> | I'm not asking that they do, I'm saying the onus is on the host to preserve this guarantee, whatever "host" is (virtual or browser, etc.) |
17:04 | <bakkot> | If we want it to be a guarantee we can write it down, but it sounds like the designers of the feature do not think it should be a guarantee, so I don't understand the model we're going for here |
17:04 | <rbuckton> | by "viable" do you mean something like "convenient"? AsyncContext that doesn't support .then would not be useful for my purposes and I don't know that I would be willing to support Stage 2 without it. Limiting it only to await makes it barely usable and results in a terrible DX for the most common case outside of await itself. |
17:05 | <Justin Ridgewell> | How can we transfer #tc39-async-context:matrix.org to the TC39 space? |
17:05 | <ljharb> | i think i can do it if you add me as an admin to the channel |
17:06 | <Justin Ridgewell> | Done |
17:07 | <bakkot> | No, I mean an |
17:07 | <ljharb> | give me a bit, i should have it done by 10 |
17:08 | <bakkot> | I am open to arguments of that form, but those would incline me in the direction of making it a requirement for hosts that any method of registering a callback which might get run out of the current microtask tick must wrap the callback with the async context which was present when the callback was registered |
17:08 | <bakkot> | (like I said, I'm ok with "all" or "none", just not "some") |
17:10 | <rbuckton> | that is: the thing you mean is "without this it will be too inconvenient to be practical", not "without this it will lack some guarantee I rely on", right? .then is too inconvenient to be practical, and too confusing for users who have been told for years that async /await is a syntactic transformation over .then . Adding something new to await would break that mental model. |
17:10 | <littledan> | Who has permissions to add rooms to the TC39 space? |
17:10 | <littledan> | We'd like to add the existing async context room |
17:11 | <Justin Ridgewell> | Jordan said he'd do it just above |
17:12 | <littledan> | (I wouldn't mind if all delegates had these sorts of permissions) |
17:13 | <littledan> | sorry, I had missed this comment. thanks, Jordan! |
17:14 | <rbuckton> | (like I said, I'm ok with "all" or "none", just not "some") |
17:15 | <rbuckton> | But within ECMA-262, it should at least flow through the built-in control-flow objects we have (namely built-in Promise and Generator objects). |
17:18 | <bakkot> | If we can't mandate "all", we can't do the "cannot escape context" thing Justin said is a requirement |
17:19 | <littledan> | "cannot escape the context" is something which a particular environment can give semantics to |
17:19 | <littledan> | it is not a meaningful thing at our level |
17:19 | <littledan> | we just have a couple cases to decide on |
17:20 | <littledan> | it is certainly meaningful within Bloomberg, and has been implemented with this case-by-case approach |
17:21 | <bakkot> | If we're not worried about whether or not it's possible to escape the context, then it's just a question of ergonomics, and that's something which we can discuss - I continue to be of the opinion that requiring every library author in the world to think about AsyncContexts is worse ergonomics than making people who are doing fancy things with .then and also with AsyncContexts think about AsyncContexts. |
17:21 | <bakkot> | I am a lot more OK with making people who are explicitly using AsyncContexts think about them than making everyone else think about them. |
17:22 | <littledan> | I am a lot more OK with making people who are explicitly using AsyncContexts think about them than making everyone else think about them. |
17:22 | <Justin Ridgewell> | If we can't mandate "all", we can't do the "cannot escape context" thing Justin said is a requirement await /Promise /setTimeout /setInterval is sufficient for us to guarantee our platform contexts is preserved |
17:23 | <Justin Ridgewell> | The dev could have their own contexts inside ours, but it'd be impossible for them to escape ours |
17:24 | <bakkot> | This just isn't the way they are used; the AsyncContext flows through a whole environment automatically and this is the entire point. |
17:24 | <littledan> | it's not something we require, it's something we allow |
17:24 | <bakkot> | That's what I keep saying - if it is in fact a requirement that it flows through the whole environment automatically, then the ergonomic differences between Promise.prototype.then and addEventListener just aren't relevant. |
17:25 | <littledan> | maybe we should shift this conversation to the AsyncContext room? |
17:25 | <bakkot> | And if it's not a requirement that the context flows automatically, then library authors now have to think about async contexts all the time. |
17:25 | <bakkot> | Sure. |
17:25 | <bakkot> | I also opened https://github.com/legendecas/proposal-async-context/issues/22 for anyone who wants to follow along. |
17:26 | <littledan> | And if it's not a requirement that the context flows automatically, then library authors now have to think about async contexts all the time. |
17:28 | <bakkot> | Let's continue the conversation in the async contexts room? |
17:28 | <rbuckton> | Also, as AsyncDisposableStack is another case of a reified async control-flow mechanism (representing an imperative version of using await ), I would also expect that the async context would automatically flow to the registered [Symbol.dispose]() method or adopt /defer callbacks when the stack itself is disposed. |
17:29 | <bakkot> | Not when the stack is disposed manually, surely? |
17:43 | <rbuckton> | Why would that not be the case? That would certainly be the case if calling dispose() on the synchronous DisposableStack , would it not? |
17:58 | <rbuckton> | Though it could be that the automatic capture might need to occur at the point the resource is registered. I admit, it is something that will need to be investigated. |
18:02 | <rbuckton> | My connection may have dropped |
18:03 | <rbuckton> | Sorry, it looks like I just lost my internet connection |
18:03 | <ryzokuken> | rbuckton: would you be able to reconnect? |
18:05 | <rbuckton> | I just restarted the router and am waiting for a connection |
18:05 | <rbuckton> | It does not look like it will be back up quickly. I can try to connect from my phone if someone else can run the slides |
18:11 | <ljharb> | https://tc39.es/proposal-symbol-predicates/ |
18:17 | <msaboff> | Rob Palmer: Is there a plan to come back to the demoting import assertions to stage 2 in this meeting? |
18:19 | <littledan> | msaboff: We want to discuss import assertions tomorrow, but we're still drafting what would be proposed. |
18:20 | <HE Shi-Jun> | can't see the screen, is that only me? |
18:21 | <rbuckton> | I am now back online, my apologies for the interruption. |
18:21 | <Justin Ridgewell> | I can see |
18:21 | <HE Shi-Jun> | reconnected and now i also see |
18:24 | <rbuckton> | shu: current semantics is the class declaration is decorated, and potentially replaced, affecting both the local and exported binding. |
18:25 | <shu> | then what's the footgun? |
18:25 | <shu> | the footgun as daniel says depends on the local being undecorated but the export being decorated |
18:25 | <rbuckton> | The biggest reason why we did not change this back in stage two was the potential do decorate both the export and the class separately. |
18:25 | <shu> | oh okay i think i get it, let me try |
18:26 | <shu> | the reason we have export-decorator ordering is for the possibility of distinguishing decorated export-only or decorated local-only |
18:26 | <bakkot> | do you figure I could write a codemod to move the export keyword before the decorators before this topic finishes while also fixing up the notes |
18:26 | <bakkot> | it can't be that hard |
18:26 | <shu> | and you're saying, not only is there no demand for that distinguishing, it is a footgun to distinguish |
18:26 | <rbuckton> | The investigation I did into "export decorators" led to the conclusion that it isn't something that could have any kind of meaningful semantics. |
18:27 | <rbuckton> | Yes. |
18:27 | <ljharb> | that's not the only reason tho. it's also because of some who believe the decorator "should" come next to the thing it's decorating, instead of an unrelated keyword being injected between them |
18:28 | <ptomato> | in GNOME, we had a strong use case for the local binding being the decorated class, not the undecorated one. although we seem to have managed to rewrite things so it doesn't matter. I gave more details in https://github.com/tc39/proposal-decorators/issues/441 |
18:29 | <littledan> | by the local binding, do you mean the binding within the class? |
18:29 | <shu> | littledan: no, the unexported binding |
18:29 | <shu> | within the module |
18:29 | <littledan> | littledan: no, the unexported binding |
18:29 | <bakkot> | I assumed the binding would be decorated either way, and "export decorators" would be decorators that applied specifically to exports in some way |
18:30 | <Richard Gibson> | yes, that |
18:30 | <shu> | okay i just don't have an intuition here |
18:30 | <shu> | bakkot: ah okay |
18:31 | <ptomato> | oh, I meant the binding within the class. disregard my comment then |
18:31 | <littledan> | We definitely had at least 8 months of plenary discussion about decorator/export ordering |
18:31 | <littledan> | in addition to the breakout groups that Jordan mentions |
18:31 | <littledan> | and in addition to extensive discussion in the regular decorator calls |
18:31 | <shu> | i remember the breakout yes |
18:32 | <littledan> | That doesn't mean it's bad to bring up! |
18:32 | <bakkot> | I assumed the binding would be decorated either way, and "export decorators" would be decorators that applied specifically to exports in some way |
18:33 | <bakkot> | man I don't care that much about this question but the idea that export is the same kind of thing as async is wild to me |
18:33 | <leobalter> | what happens to export default? |
18:33 | <leobalter> | rbuckton: ^^ |
18:33 | <rbuckton> | @dec export default class {} |
18:33 | <shu> | Justin Ridgewell: it's a strawperson (meaning in the hypothetical sense) footgun |
18:34 | <shu> | like, nothing has this footgun today, but if we started distinguishing semantics depending on where the decorator appears, then it becomes a footgun |
18:35 | <bakkot> | does anyone know offhand of a library which is using @dec export class {} style decorators so I can develop my codemod against it |
18:36 | <shu> | bakkot: it's a good question, i added a q item |
18:36 | <leobalter> | is anyone actually advocating for the distinct semantics? Or is this just about syntax placement? |
18:38 | <bakkot> | nest , apparently |
18:38 | <bakkot> | https://github.com/nestjs/nest |
18:38 | <rbuckton> | is anyone actually advocating for the distinct semantics? Or is this just about syntax placement? |
18:38 | <nicolo-ribaudo> | Fwiw, Babel's most used decorators versions is the "legacy" one, which has decorators before export . The new versions have a decoratorsBeforeExport option to choose, and according to GitHub search it's set to true and false more or less the same number of times |
18:38 | <bakkot> | https://github.com/nestjs/nest/blob/master/sample/33-graphql-mercurius/src/app.module.ts |
18:39 | <leobalter> | Thanks for the clarification, rbuckton. So this seems like only about syntax ergonomics |
18:39 | <nicolo-ribaudo> | https://github.com/search?q=decoratorsBeforeExport%3A+true&type=code and https://github.com/search?q=decoratorsBeforeExport%3A+false&type=code |
18:39 | <shu> | wait what |
18:39 | <ljharb> | that it's set the same number of times is a pretty strong counterargument to "nobody's demanded a different ordering in 8 years" imo |
18:39 | <shu> | i... would've figured we definitely don't have appetite to add keywords for such scoped semantics like "writable namespace exports"? |
18:40 | <shu> | "we want syntax for more static analyzability" is pretty true of a lot of things that don't meet the syntax bar, no? |
18:41 | <nicolo-ribaudo> | Well, but most people are using the legacy version that only supports decorators before exports. I'm saying this based on how much issues/discussions we get about decorators, but also searching for |
18:41 | <bakkot> | all decorators |
18:41 | <ljharb> | regarding Symbol predicates: i'd love some committee feedback on https://github.com/tc39/proposal-symbol-predicates/issues/9. Namely, to me, a predicate never throws and only returns a boolean - and in this case, eg "isRegisteredSymbol" is asking "is this a symbol and also a registered one". michael seems to read it as asking "is this symbol a registered symbol", implying it should throw on non-symbols. does anyone have thoughts on this? Please add them to the issue! |
18:41 | <bakkot> | all decorators meet that bar |
18:41 | <ljharb> | oh sure. most people don't have the choice tho |
18:42 | <ljharb> | it's telling that when given the choice, it's about an even split |
18:46 | <shu> | side question: in matrix, now that there are threads, how do i find threads i have unread messages in? |
18:46 | <bakkot> | my answer so far is, scroll |
18:46 | <Michael Ficarra> | I was trying to do this same thing! |
18:46 | <bakkot> | which is not... ideal |
18:46 | <shu> | well guess some messages are staying unread |
18:47 | <Michael Ficarra> | oh I think I just figured it out |
18:47 | <Michael Ficarra> | close any open thread if you have one open, then click this button: |
18:47 | <Justin Ridgewell> | Click the thread icon in the top right of the app |
18:47 | <Michael Ficarra> | in the top right |
18:48 | <shu> | ooo |
18:48 | <shu> | i can click on < Threads |
18:48 | <Richard Gibson> | and when you still can't find it, right click on the room and Mark as read |
18:49 | <ljharb> | there's also a "back" left chevron in the upper left corner of a thread window that takes you there |
18:49 | <Michael Ficarra> | oh, also this is helpful: |
18:50 | <Michael Ficarra> | the default seems to list every thread, which I can't imagine wanting |
18:51 | <rbuckton> | I see this in Element: |
18:51 | <Michael Ficarra> | ugh, it doesn't remember that preference though |
18:51 | <leobalter> | Option 2 kills the idea of distinct semantics. Syntax positioning can eventually be set as a linter preference for consistency within user code. |
18:51 | <leobalter> | I really like option 2 for this reason. |
18:52 | <ljharb> | not to me in this context, where "fixed" is much more likely to mean "unchanging" than "no longer broken" |
18:52 | <ljharb> | option 2, however, ensures burden on linters/formatters, and creates style debates, forever |
18:52 | <Michael Ficarra> | but... it's the same word |
18:53 | <ljharb> | the tense makes the difference to me |
18:54 | <bakkot> | ok here is a codemod to go from TS-style to ES-style placement https://github.com/bakkot/export-decorated-class-codemod |
18:54 | <ljharb> | especially in programming, where (it seems to me that) "fix" is relatively rare but "fixed" is relatively common |
18:54 | <bakkot> | thank you to whoever has been fixing the notes while I looked away to write this |
18:54 | <Michael Ficarra> | bakkot: you were supposed to do both at the same time! |
18:54 | <bakkot> | I was doing some amount of it |
18:54 | <bakkot> | but then kind of forgot |
18:55 | <Michael Ficarra> | there's also https://hackage.haskell.org/package/base-4.17.0.0/docs/Data-Function.html#v:fix |
18:56 | <rbuckton> | export default class C {} is a default export and a C class declaration. |
18:57 | <rbuckton> | export default (class C {}) is a default export for an expression |
18:57 | <shu> | i think transferAndFixLength makes it sound like it does a transfer, then does an operation to turn a resizable buffer into a fixed length one |
18:57 | <yulia> | I'm off for the evening, if anyone has any concerns or wants to get a Mozilla opinion, we have iain taking over for the last hour! (acronym is IID) |
18:57 | <shu> | but that "fixing the length" operation isn't something that exists |
18:57 | <leobalter> | option 1 is really nice to avoid confusion from ljharb the style discussion burden option 2 seems not enough for me to object. It's already in the nature of the language |
18:57 | <shu> | also it may be ambiguous between what length you're fixing |
18:57 | <shu> | the receiver or the return value |
18:58 | <ljharb> | rbuckton: what about export default class {} ? |
18:58 | <HE Shi-Jun> | prefer option 1 |
18:58 | <littledan> | rbuckton: what about |
18:58 | <ljharb> | with what binding name, "default"? |
18:58 | <nicolo-ribaudo> | With option 1 (decorators only before export), would export default @dec class A {} be a valid export of a class expression? |
18:59 | <ljharb> | and what about export default @noopDecorator class {} ? |
18:59 | <bakkot> | With option 1 (decorators only before export), would |
18:59 | <rbuckton> | It creates an export binding named default , as evidenced by import { default as Foo } from "module" |
18:59 | <nicolo-ribaudo> | With option 1 (decorators only before export), would |
18:59 | <rbuckton> | We would disallow that and require parens. or at least, that would be my preference |
18:59 | <littledan> | Someone is typing very loudly |
19:00 | <rbuckton> | sorry, that was me |
19:00 | <littledan> | and what about |
19:00 | <bakkot> | I guess we already lookahead prevent class and function in that position, so adding decorators there is presumably easy |
19:00 | <ljharb> | This is very much a declaration. This is clear in the decorator spec. default ? |
19:00 | <bakkot> | no you don't get a local binding from anonymous classes |
19:01 | <bakkot> | with or without decorators, export class {} has no user-observable local binding |
19:01 | <leobalter> | ryzokuken: can we do a heat check for both options? |
19:02 | <Michael Ficarra> | interesting |
19:02 | <rbuckton> | with or without decorators, export class {} without either default or a name is illegal |
19:02 | <Michael Ficarra> | the thing I didn't like about the name we went with is that it feels like it drops off without mentioning a noun |
19:03 | <Rob Palmer> | We will need very clear wording for a temp check. |
19:03 | <bakkot> | sorry, export default class {} , of course |
19:03 | <Michael Ficarra> | "transferTo" needs to be followed by a thing |
19:03 | <Michael Ficarra> | but it's missing a thing |
19:03 | <littledan> | ryzokuken: can we do a heat check for both options? |
19:03 | <bakkot> | hey now, my codemod is correct |
19:03 | <ryzokuken> | we'd need a well-defined problem statement for a temp check |
19:03 | <bakkot> | it keeps trivia |
19:04 | <leobalter> | We will need very clear wording for a temp check. |
19:04 | <bakkot> | it keeps every single character that was in the source |
19:04 | <bakkot> | just moves them around |
19:04 | <bakkot> | writing correct codemods is not that hard |
19:05 | <Rob Palmer> | Please phrase the question as a temp-check compatible question. https://github.com/tc39/how-we-work/blob/main/presenting.md#temperature-checks |
19:05 | <Rob Palmer> | Do you want multiple temp checks? |
19:05 | <leobalter> | multiple, for each option proposed |
19:06 | <ljharb> | presumably TS could retain the ability to parse decorator-before-export, and give a specific helpful message in that case too telling them how to fix it? |
19:06 | <leobalter> | The goal is to help me identify what concerns are counted as strong objections |
19:06 | <Rob Palmer> | Mark has already said he is strongly opposed to Option 1 |
19:07 | <ljharb> | hm, i just got booted from the meeting, is that just me? (probably just me) |
19:07 | <ryzokuken> | could you try reconnecting? |
19:08 | <ljharb> | nah it was just my local internet. comcast fun times. |
19:08 | <leobalter> | and Mark seems to be ok with Option 2. Perhaps the heat check might just aim to this option |
19:09 | <leobalter> | Rob Palmer ryzokuken if the TS team is fine, can we just do a temp check for support/objection to Option 2? |
19:09 | <rbuckton> | I also apologize for the keyboard noise. I thought Jitsi had NVidia Broadcast set for my input audio, which normally filters that out, but it was not. |
19:09 | <Rob Palmer> | we don't have time for it now |
19:09 | <leobalter> | this would be a very valuable information from this discussion |
19:10 | <leobalter> | leaving a workable path to move it forward |
19:10 | <littledan> | leobalter: You still haven't formed a clear question for the temperature check |
19:10 | <leobalter> | I did |
19:12 | <Rob Palmer> | We'll do the temp check tomorrow. I encourage the champions (and everyone) to read the guidance on how to request and frame temp checks. We have repeatedly had problems when people request them on-the-fly. It is much better to pre-plan them. https://github.com/tc39/how-we-work/blob/main/presenting.md#temperature-checks |
19:12 | <pzuraq> | re: the migration path, there are going to be some decorators which cannot be migrated 1-1 is the core issue. The new spec is just less powerful than the old one in some core ways. In these cases, it's likely that teams will try to migrate in parts, and possibly use both specs in different files (using a pragma to distinguish which spec to use) |
19:12 | <pzuraq> | depending on the size of the codebase, of course |
19:13 | <bakkot> | Yeah, but you can codemod one file at a time, surely |
19:13 | <pzuraq> | minimizing syntax changes minimizes the number of files that need a pragma, because files when the syntax is the same can feature detect based on usage |
19:13 | <shu> | so is that an argument that this particular ordering is better served by codemods, or not by codemods and by changing the language...? |
19:13 | <leobalter> | Rob Palmer: I'm sorry about the last minute request. I did a quick read on the doc. I'm not the one proposing the change, so I'll drop my requests anyway. |
19:14 | <pzuraq> | bakkot: some of these changes will not be codemoddable |
19:14 | <bakkot> | pzuraq: the only change we're discussing is syntax, which is definitely codemoddable |
19:14 | <pzuraq> | e.g. transitioning to accessor |
19:14 | <shu> | yes i'd prefer this discussion to be scoped to this particular change |
19:15 | <shu> | not fully generic ones, or even generic ones about decorators |
19:15 | <pzuraq> | in that case, yes that one exact change is very codemoddable |
19:15 | <shu> | unless the fullness of transitioning to ES decorators is the point you're trying to make, which i can appreciate |
19:15 | <shu> | but i didn't get that point from TS |
19:15 | <pzuraq> | yes, that is the point I'm trying to make |
19:16 | <leobalter> | danielrosenwasser rbuckton pzuraq I don't think I'm available tomorrow. FWIW, option 2 works for me. I +1 the preference for XOR positioning. I also +1 to blocking decorator before ambiguous class expr. |
19:16 | <danielrosenwasser> | what do you mean by "blocking decorator before ambiguous class expression"? |
19:16 | <bakkot> | if we do the XOR thing that implies export default @dec class A{} is a declaration, not an expression |
19:17 | <leobalter> | https://matrix.to/#/!WgJwmjBNZEXhJnXHXw:matrix.org/$gTO6aM4DVgUXJaskNyv4jIqimo85LHbMtYxA32NT2-c?via=matrix.org&via=igalia.com&via=mozilla.org |
19:17 | <bakkot> | if we do decorators-only-before-export then export default @dec class A {} is a default export of a class expression, under some possible ways of writing the spec |
19:17 | <bakkot> | in the decorators-only-before-export world that should be illegal, probably |
19:18 | <leobalter> | export default @dec class A {} |
19:19 | <danielrosenwasser> | I don't get it, the proposal already does a "lookahead ∉ {... class , @ } |
19:20 | <bakkot> | "some possible ways of writing the spec" |
19:21 | <bakkot> | if it is already prohibited I assume everyone is happy |
19:21 | <danielrosenwasser> | actually, we might be wanting the same thing leobalter |
19:22 | <leobalter> | yes! I'm just sharing my support or whatever goes as my Salesforce / LWC position |
19:22 | <leobalter> | LWC does not use class decorators yet, but the feedback comes in from the team. |
19:23 | <bakkot> | yes, that is the point I'm trying to make |
19:23 | <bakkot> | I there's migration pain involved in getting the semantics within the decorator right, but the codemod still seems basically trivial. |
19:24 | <leobalter> | I do not oppose to option 1 either. Option 2 is just my preference considering the discussion the committee had. |
19:24 | <shu> | btw what is a codemod |
19:24 | <shu> | is it "just" a transform |
19:24 | <shu> | or does it have more bells and whistles attached? |
19:24 | <bakkot> | in my case, just a source rewrite |
19:24 | <bakkot> | string-to-string |
19:24 | <pzuraq> | teams will have to remove some decorators and change the invocations of others, and a codemod will not be able to tell based on usage |
19:24 | <pzuraq> | e.g. a dev will have to manually look at each decorated field to see if it needs accessor |
19:25 | <bakkot> | yeah, I agree that a codemod cannot help you with the thing where the semantics are different |
19:25 | <bakkot> | but neither can any particular choice of syntax here |
19:25 | <pzuraq> | it can reduce what is going to be a very painful transition |
19:25 | <pzuraq> | for some teams |
19:25 | <pzuraq> | on top of the other benefits, e.g. documentation costs |
19:26 | <bakkot> | I just don't see the "after going through every decorator in this file and migrating it so it's going to be ES-semantics-compatible, also run a codemod after" as being a noticeable change in the amount of pain |
19:26 | <bakkot> | I agree the transition is painful in general but not that this particular thing is more than a trivial additional amount of pain |
19:32 | <bakkot> | I suspect people probably would be surprised that for await does an await on break , but this doesn't actually lead to the code doing a different thing than they expect so it's fine |
19:32 | <bakkot> | that will not be true of this syntax |
19:33 | <littledan> | that will not be true of this syntax for await . |
19:33 | <bakkot> | no, because the RHS does not get awaited |
19:34 | <bakkot> | like if you see using await x = y and assume that y is awaited, you will be wrong |
19:36 | <shu> | i must be dumb because all these arguments still cut both ways to me |
19:36 | <shu> | but maybe that's fine, that means we just need to choose one? |
19:36 | <littledan> | i must be dumb because all these arguments still cut both ways to me |
19:36 | <bakkot> | I think that there are decent theoretical arguments in both directions, and also that people can learn either syntax eventually |
19:37 | <bakkot> | which means that I would decide this by, which do we expect to cause less initial confusion, emperically |
19:37 | <bakkot> | my expectation is that async using will cause less confusion than using await |
19:37 | <bakkot> | given infinite resources I would run a study but I don't think it's worth running a study |
19:38 | <bakkot> | that said I am not, like, absolutely committed to the async using syntax. I think it will result in less confusion but it's not the end of the world if more people are confused. |
19:38 | <shu> | yeah @bakkot's is my exact intuition, but it's also just intuition |
19:39 | <shu> | more self servingly i don't want to have to explain "well actualy, this is why await is not nonsense" to developers |
19:40 | <littledan> | Honestly I don’t know who is being dismissive to user confusion here |
19:41 | <littledan> | +1 to Mark’s “both sides” analysis |
19:44 | <peetk> | sorry if i missed someone already answering this but why is the for await case not precedent for await not always meaning "await right here"? |
19:45 | <bakkot> | peetk: with for await all the await ing happens scoped to that statement |
19:45 | <bakkot> | that is not true for using await |
19:45 | <bakkot> | that is, right now await , including in for await , means "there's some awaiting in the rest of this statement" |
19:46 | <peetk> | i see. that's an expanded definition of "right here" tho |
19:47 | <bakkot> | not one which causes confusion in practice, though |
19:47 | <bakkot> | but I still expect using await to cause confusion in practice |
19:47 | <peetk> | is for await widely used enough to gauge how confusing it is lol? |
19:47 | <bakkot> | no matter how good our theoretical justifications are |
19:47 | <Justin Ridgewell> | Is it expanded? It's performing an await on that line before the block on the next line begins execution |
19:47 | <bakkot> | I think so? for await sees a fair bit of use IME |
19:47 | <peetk> | fair enough |
19:48 | <bakkot> | https://nodejs.org/api/readline.html#example-read-file-stream-line-by-line etc |
19:48 | <peetk> | my experience is normie devs are not even sure it's valid javascript when they see it but shrugs |
19:50 | <peetk> | maybe that's changing these days idk |
19:50 | <bakkot> | new syntax always takes a while to catch on, yeah |
19:54 | <peetk> | Is it expanded? It's performing an await on that line before the block on the next line begins execution |
19:55 | <bakkot> | the proposed interpretive principle is "await signals that it + its RHS will do some awaiting", and that's true in the current syntax |
19:55 | <bakkot> | at least assuming the body of the loop is considered to be "its RHS", but since it's in the same place as the for token and the body of the loop is surely the RHS of the for , that seems legit |
19:56 | <peetk> | i guess i was responding to a different proposed interpretive principle which was "await means some awaiting happens right here" |
19:57 | <bakkot> | I think the body of the loop counts as "right here" |
19:57 | <shu> | hm that feels kind of intuitive to me actually |
19:58 | <shu> | hypothetically if the syntax was using await (bindingExpr) { } , that seems perfectly clear |
19:59 | <ljharb> | that seems really nice, but i think ron had use cases for wanting to do some action which might throw, queue up the result, and then have it disposed at block exit, which i'm not sure would work with that form? |
19:59 | <bakkot> | hypothetically if the syntax was try instead) and we moved away from it |
19:59 | <shu> | right i know |
19:59 | <peetk> | I think the body of the loop counts as "right here" await in for await has always felt me like sticking an await in a truly random place to vaguely signal that some awaiting happens somewhere, somehow, and i had to learn by rote where actually it happens. |
20:00 | <shu> | i'm just saying what you said about the for body counting as "Right here" rings true to me |
20:00 | <bakkot> | ah sure |
20:01 | <peetk> | i guess to sharpen my point it's not as though this interpretive principle helps people understand what exactly is being awaited, when, at least in the case of for await . |
20:01 | <bakkot> | hm. I think I would be much, much more surprised if for await did awaiting not in that statement |
20:01 | <bakkot> | like, I agree you have to learn the rules for where within the statement |
20:01 | <ljharb> | there's an interleaving point in an async function , that's invoked, even with no await present, no? same with the proposed async do {} ? or is that incorrect because async functions (and async do blocks) run synchronously until the first await, or return? |
20:01 | <peetk> | but the non-locality of using is sort of already baked into using |
20:01 | <bakkot> | but it's scoped |
20:04 | <bakkot> | async function's its a bit weird because of the return, but async do {} there's no interleaving unless you explicitly await in the body of the do |
20:05 | <littledan> | FWIW I'm not sure that enforcing turn boundaries was the right call, but this is perfectly consistent with our overall design so it seems locally reasonble |
20:05 | <ljharb> | so async do { Promise.resolve({}) } has no interleaving point? it produces a promise? |
20:06 | <littledan> | so |
20:06 | <ljharb> | gotcha |
20:06 | <bakkot> | ljharb: async do {} always produces a promise |
20:07 | <bakkot> | async do {} is very nearly pure sugar for in immediately invoked async arrow |
20:07 | <ljharb> | right, and in my example it'd flatten the promise and end up with a new promise for that arbitrary object |
20:07 | <bakkot> | ah, yes, that's right |
20:10 | <shu> | bakkot: get gilad bracha to be the tiebreaker |
20:10 | <shu> | need more opinions |
20:10 | <bakkot> | gilad is happier not thinking about this kind of question, I believe |
20:11 | <bakkot> | which, relatable |
20:12 | <shu> | i only have envy for that position |
20:12 | <shu> | but the world still cares |
20:12 | <shu> | why shouldn't the burden be thrust upon him |
20:21 | <peetk> | i'm 92% kidding here but would people who disprefer using await like it better if using /using await were spelled will dispose /will await dispose respectively? |
20:24 | <littledan> | umm no? I like using . |
20:25 | <peetk> | i meant "would it address the specific 'await means here' concern" (and also it is definitely not a real proposal for many reasons) |
20:25 | <rbuckton> | We already had two/three-keyword declarations in this proposal. Cutting back to one/two-keywords was a win. |
20:26 | <rbuckton> | We could potentially use await using x = ... instead. I opted against it because of the cover grammar necessary to try to keep LR1, but it's an option. |
20:26 | <littledan> | i meant "would it address the specific 'await means here' concern" (and also it is definitely not a real proposal for many reasons) await -doubters would want to see the await at the end of the block, hence the async which is always a bit more action-at-a-distance |
20:27 | <littledan> | We could potentially use async using . |
20:27 | <rbuckton> | await using x = is 1:1 the C# syntax. |
20:27 | <rbuckton> | It indicates what you are awaiting is the using , possibly more strongly than using await does. |
20:28 | <littledan> | yeah, maybe. Adding more cover grammars seems good to avoid. |
20:28 | <rbuckton> | But I also chose using await to mirror for await in ordering. |
20:29 | <rbuckton> | yeah, maybe. Adding more cover grammars seems good to avoid. |
20:29 | <ljharb> | that's what the priorities of constituencies (sp) would say, i think |
20:29 | <peetk> | yeah my thinking was that using already creates the action-at-a-distance, which is why it feels natural to me (or at least, not hideously unnatural) for it to project the await to also be non-local |
20:30 | <bakkot> | IIRC, bakkot 's position is that the syntax complexity shouldn't matter as long as we achieve the right syntax. bakkot, is that correct? |
20:33 | <rbuckton> | await [NLT] using [NLT] BindingIdentifier wouldn't require infinite lookahead. The only problem is a recent editorial change I made to use an early error to forbid BindingPattern to avoid differing uses of the Using production parameter, though that's also easily changed. |
20:34 | <rbuckton> | (though the NLT between await using isn't strictly necessary, I'd still they rather not be split across multiple lines) |
20:36 | <bakkot> | right, I think await using x would be acceptable from a spec-complexity point of view, if we thought that was best for users of the language |
20:36 | <bakkot> | that is, if we thought that was best, I wouldn't want to choose some other syntax just because of spec complexity reasons |
20:37 | <bakkot> | (personally, if we are sticking with await , I lean a little more towards using await , so it's harder to confuse with a statement-positioned AwaitExpression.) |
20:46 | <rbuckton> | Thanks all for the feedback on the Async Resource Management proposal. My apologies for the technical issues today, hopefully they won't repeat themselves tomorrow. |
21:34 | <rbuckton> | erights, et al. Apparently I misspoke regarding Function.prototype.toString . The specified semantics include class decorators in a class's .toString() , but do not include method decorators in a static method. The conclusion to that issue (https://github.com/tc39/proposal-decorators/issues/109) indicates it should be reopened if there are concerns with the resolution, however. |
21:36 | <rbuckton> | There was a comment in that issue about how eval(className.toString()) could produce a copy of that class, but that wouldn't be true in the case of a class decorator capturing the .toString() of its target, because its still in the middle of evaluation and the eval result wouldn't be the same as the class that the class decorator sees. |
21:41 | <bakkot> | do people in the decorator-before-export camp think that decorators should also come before the
|
21:41 | <bakkot> | I assume not but am not entirely clear on what the relevant distinction between return and export is here |
21:46 | <bakkot> | I guess I don't actually care enough to argue about this, ignore me |
21:47 | <bakkot> | the main outcome I would like to see here is typescript accepting only standard syntax (unless a flag is passed), whatever we ultimately settle on |
21:55 | <snek> | i think we probably should not have allowed this case at all, in either order. declare your thing explicitly and then export/return/etc it |
21:59 | <rbuckton> |
export (which has non-local effects that occur before evaluation) and return (which does not). |
22:00 | <ljharb> | i think we probably should not have allowed this case at all, in either order. declare your thing explicitly and then export/return/etc it |
22:01 | <rbuckton> | IIRC, that was mentioned at one point. |
22:01 | <snek> | this feels like a garbage in garbage out sort of situation, trying to create meaning from chaos |
22:02 | <snek> | did typescript actually ship the new decorator impl at this point |
22:04 | <rbuckton> | We talked about the return thing back in 2018 as well: https://github.com/tc39/proposal-decorators/issues/69#issuecomment-429202746 |
22:04 | <rbuckton> | In our 5.0 beta, yes, but there are a few moving pieces still that we're trying to resolve this meeting. |
22:05 | <rbuckton> | lol that is a previously unmentioned fourth option, i guess |
22:10 | <snek> | maybe we can yeet this before anyone starts writing code with it 😄 |
22:11 | <ljharb> | i mean that's actually reasonable in that it would leave the question open and remove any time crunch |
22:11 | <ljharb> | but it would still cause all the same churn that the TS folks don't want |
22:17 | <snek> | i will need to read through those threads to understand why we want to allow it in the first place |
22:36 | <snek> | it doesn't seem like any super strong motivation for this was given lol |
22:43 | <snek> | if i'm not around tomorrow during the discussion can someone mention just not allowing this at all |
22:54 | <rbuckton> | if i'm not around tomorrow during the discussion can someone mention just not allowing this at all |
22:54 | <snek> | the latter |
22:55 | <rbuckton> | I'm sure someone will bring it up, but it certainly doesn't solve our motivation for bringing this back to plenary. |
22:55 | <snek> | I wasn't able to find motivation for why we want it in the first place, so it seems a lot simpler to cut it out entirely |
22:56 | <snek> | well I found one comment from Waldemar that seemed against not having it at all |
22:56 | <snek> | but it didn't include a reason |
22:56 | <rbuckton> | Not being to decorate an exported class all at once isn't a great developer experience, especially if you compare this to similar capabilities in other languages. |
22:57 | <snek> | you can export it, you just have to use the separate export declaration |
23:02 | <rbuckton> | While it isn't an outcome I'd prefer, that was an outcome I suggested since it would allow us to break out this concern from the main proposal and pursue it separately. It would still require a code mod, unfortunately, but that impact might be somewhat lessened by the fact there are many TS projects that can't switch to native decorators due to the lack of parameter decorators (which I plan to propose as a follow up this year), the inability to decorate an entangled pair of get /set methods (which would be solved by the Grouped and Auto-Accessors proposal), or the inability to register a static extra initializer from a non-static member. Its possible we could resolve this separately by the time those proposals reach Stage 3 and potentially avoid a code mod as well if the ordering is consistent. |
23:03 | <snek> | personally I don't think it should even be pursued as a followon but at the very least it seems like giving more time is reasonable |
23:03 | <snek> | 🤷♂️ smth for committee to discuss I guess |
23:22 | <littledan> | I think it is important to allow decorated classes to be exported… our language features should generally compose |