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>

"Scala is unique in that it only provides _, but that placeholder refers to a different parameter on each use. So _ > _ is a binary predicate that checks if the first parameter is greater than the second. Boost.HOF does the same with its unnamed placeholders [boost.hof.unnamed]."

04:17
<Ashley Claymore>
Wild!
04:19
<bakkot>
honestly kinda like scala's _
04:32
<rbuckton>
honestly kinda like scala's _
I mean, PFA would do this with ?, 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 )
What are you talking about?
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
ljharb: Could you clarify how you feel about "ID", an acronym of "identity document"?
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?
check the page history, someone changed ID to Id yesterday. 🫥
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.
Good point, we usually do that and I hadn’t made that fix
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. 🫥
Sorry if that wasn’t clear from the tone. That was me in order to point out how unreliable Wikipedia is for topics where there is controversy. It’s ok as a lookup for common knowledge stuff, but wherever there is ongoing discussion wikipedia is useless.
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).
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: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.
I agree. The failure mode we have is very unfortunate, and this case highlights it. I think this indicates a need for us to reconsider our process.
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?
yes, there will be a responsibility for the host environment to make sure the event-scheduling APIs it introduces (like 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
the await pause is the point where you are scheduling a thing to be run on a subsequent tick, so that seems consistent?
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
good to ask. Though presumably all APIs could be wrapped in userland anyway. So technically nothing also stopping hosts doing that?
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
https://github.com/legendecas/proposal-async-context/issues/19 many events are emitted by runtimes asynchronously
15:22
<littledan>
that becomes a lot easier if/once AsyncWrap is standardized
Yes, obviously the platform can't do anything if we don't add AsyncContext :) I was just nit-picking
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)
sorry: the same behavior as what?
15:26
<littledan>
Yeah, that's the point of an alternative option captureAsyncContext of addEventListener
well, there are separate questions about whether we want to give programmatic control, and what the default should be for each event.
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
this is definitely sometimes not true. It is really not the right answer for the unhandled rejection event, for example (we are discussing this in an issue)
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
I guess I don't know what those words mean
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
thinking about this more, I guess the fix is, if you are passing a context to someone else you manually .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 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
I mostly agree, with the exception of .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. 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.
only if you have a bunch 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.
it's still preserved either way, the sugar just gets a little more complicated
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 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.
that seems pretty straightforward to explain, for people using async contexts? "if you pass a callback, you need to wrap it" is not a hard rule to learn
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?
done
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 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
IIRC, 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 AsyncContext, especially if that code calls into a third-party package that might use .then.
does it? if you pass a callback to the library you need to wrap it. but you need to anyway because the third-party package might schedule the callback to get called in some way other than .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
I don't agree its simpler. Simpler is not having to wrap callbacks for things that are, categorically, async continuations.
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.
huh, have you considered arguing this to the Node.js APM ecosystem?
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 await promise because I have to patch .then, I will riot)
My point is that you don't have to patch .then
16:15
<Justin Ridgewell>
As the platform, I do
16:15
<bakkot>
... why?
16:15
<bakkot>
Assuming there's implicit wrapping in syntactic awaits
16:15
<Justin Ridgewell>
The whole console.log use case in the slides
16:15
<littledan>
Assuming there's implicit wrapping in syntactic awaits
sure, I agree, in theory we could fork the semantics here
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
I think bakkot is saying, you could go patch that
16:16
<bakkot>
I have to preserve across any promise usage, because anything else will break an important API for us
Can you give an example?
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
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: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.
"calling into questionable code" meaning calling into... any code?
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>

I am not convinced that's something worth designing for.

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
hm, also, I'm confused about this example. assuming that 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 transferAndFixLength is better than transferToFixedLength
"fix" as a verb there is pretty overloaded
16:25
<littledan>
"calling into questionable code" meaning calling into... any code?
I don't believe people should or do compose code in a perfectly defensive way all the time
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
If you hand your callback to someone else, and there is literally no way they could schedule the callback to run with a different context than the one which existed when you handed the callback to them, I am also fine with that
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
Why didn't you put yourself on the queue?
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
OK, in this case, I don't see your advice for waiting more for Stage 3 as something actionable for me if I ran into a simlar case
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 promise.then() must preserve context
yes, and also every other possible way of scheduling a callback
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
I agree, that's the behavior that I want
16:31
<littledan>
Michael, this is exactly what we introduced the "note dissent that's non-blocking" part for
so I'm wondering what's not going to work about that
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
To me, the value of advancing today outweighed the value of potentially coming up with a slightly better name. If it had not, I would have spoken up formally.
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?
Promise instances are reified async control flow operations.
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"
That's a false equivalence. Promises are sugar for CPS, of which callbacks are a part, not the entirety.
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 Id issue, are you available during the break today?
absolutely, thanks
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
can we aim for the first half of the break?
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 AsyncContext. That is, as I understand it, literally the entire point of this feature.
That's not entirely true. If you, as a library author, are using language-specified async control flow capabilities (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
I'm not refuting that. My position is that 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 !
I think we need to transfer it to the TC39 space first, then open up registrations to any space members
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) 🥺
Sorry about that, please try again
17:02
<littledan>
I think we need to transfer it to the TC39 space first, then open up registrations to any space members
no, you literally just left it private
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
no one should have to know that XHR or setTimeout are not "built in" in the same sense that Promise is
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"?
No, I mean an 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 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.
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?
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 yes. Requiring manual wrap for .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")
I don't imagine "all" is practical either, not without running afoul of shu's performance concerns. My position is that it should be considered on a case-by-case basis by hosts whether async context should automatically flow. Perhaps we can provide guidance on what cases make sense, but I'm not sure we could mandate "all".
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.
This just isn't the way they are used; the AsyncContext flows through a whole environment automatically and this is the entire point.
17:22
<Justin Ridgewell>
If we can't mandate "all", we can't do the "cannot escape context" thing Justin said is a requirement
Our platform only has so many ways for devs to do async tasks, so just having 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.
Only if we require of hosts that every mechanism of scheduling callbacks flows AsyncContexts, including event listeners and unhandled promise rejection handlers and so on.
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.
Yes. Actually, having just one mechanism here as opposed to several should simplify things for library authors who are already living this reality in environments with a version of this feature.
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
yeah I guess that part is clear
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
(e.g. as jrl says on the queue, make the binding writable from the outside which would be kind of neat I guess)
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?
We are stating that the one major reason that changing the syntax was blocked some time back was to allow for the potential of "export decorators", which we do not believe to be a viable option. If that is no longer a blocking concern, then we want to discuss changing the ordering.
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 babel legacy: true on GitHub gives a lot of results -- https://github.com/search?q=babel+legacy%3A+true&type=code

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 export vs export default. The @decorator will always be placed without confusion.

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 export default class {}?
declaration
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 export default @dec class A {} be a valid export of a class expression?
oh god
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 export default @dec class A {} be a valid export of a class expression?
I guess we would need a lookahead restriction to disallow it
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 export default @noopDecorator class {}?
This is very much a declaration. This is clear in the decorator spec.
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.
and it makes a local binding 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 {} has no user-observable local binding
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?
We have 3 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.
I'd like to hear how people support or object to Option 1 and Option 2. The XOR might be discussed after?
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
for every file, decorators in that file will either be written using TS's old semantics or ES semantics. If you are choosing for that file to use ES semantics, it is not that hard to codemod that file, I would think. if you're changing the semantics for your whole project at once, you can run the codemod for your whole project at once.
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
Hmm, I think this will "just work" in practice, just like all the stuff around 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
Yeah I kinda think either one could be learned and work
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 awaiting 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
doing real back of the envelope folk dev psychology here but imo the await would need to be inside the parens to visually cue that an await happens on each iteration, per the proposed interpretive principle
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 using await (bindingExpr) { }, that seems perfectly clear
it used to be approximately that (with 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"
i disagree. the 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 async do { Promise.resolve({}) } has no interleaving point? it produces a promise?
I think it wouldn't; the interleaving point would come if you awaited that
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)
Well, I think 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 await using x = ... instead. I opted against it because of the cover grammar necessary to try to keep LR1, but it's an option.
Yeah,I was wondering about that option too, but I think that wouldn't address the concerns of the people who prefer 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.
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: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?
not that it should matter literally zero - it is important not to force engines to do infinite lookahead, for example - but as long as the thing is reasonably implementable, yes
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 return when you're returning a class? as in

@dec
return class {}
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>

do people in the decorator-before-export camp think that decorators should also come before the return when you're returning a class? as in

@dec
return class {}
Certainly not. There is a clear semantic difference between 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
lol that is a previously unmentioned fourth option, i guess
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
Mentioned here: https://github.com/tc39/proposal-decorators/issues/69#issuecomment-429490497
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
Can you clarify what you mean by "this" in both of the two comments above? Not allowing this change? Not allowing decorators+export in either order?
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