03:16 | <ljharb> | that aspect may not have been made clear to those objectors tho; asynccontext is a tough proposal to grok imo |
04:00 | <kriskowal> | since then, i suppose those objectors have gotten over the philosophical objection that it was tantamount to unacceptabel dynamic scope, given the enthusiasm about AsyncContext |
04:02 | <kriskowal> | Enthusiastic even, since we need such a thing for causal tracing. |
04:11 | <kriskowal> | the other half of what they do seems to be making it so the dynamic import in |
04:12 | <kriskowal> | Also https://github.com/tc39/ecma262/issues/3160 |
06:00 | <Mathieu Hofman> | since then, i suppose those objectors have gotten over the philosophical objection that it was tantamount to unacceptabel dynamic scope, given the enthusiasm about AsyncContext |
06:03 | <Mathieu Hofman> | That is the same kind of concern I raised with the Signals proposal as currently designed. |
09:39 | <Andreu Botella> | I'd like to clarify that exposing AsyncContext as a feature is not the same as allowing a host or the 262 spec itself to use the AsyncContext mechanism to create a variable and make the state of that variable observable in any way. That is still completely unacceptable from an Ocap and dynamic scoping pov. |
11:28 | <littledan> | I believe the dynamic import should be resolved in the context of the script associated with the environment record internal slot on the globalThis.eval in the lexical scope. Else, bug. |
11:29 | <littledan> | That is the same kind of concern I raised with the Signals proposal as currently designed. |
11:29 | <littledan> | I'd like to clarify that exposing AsyncContext as a feature is not the same as allowing a host or the 262 spec itself to use the AsyncContext mechanism to create a variable and make the state of that variable observable in any way. That is still completely unacceptable from an Ocap and dynamic scoping pov. |
11:31 | <littledan> | And if this is terrible for JS to do, is it terrible for the web to do? |
13:46 | <Mathieu Hofman> | I don't fully understand the whole incumbent realms thing, but from what I've looked at, it doesn't seem like that would be any different from having a userland library and scheduler that uses AsyncContext variables under the hood to know whether to expose something or other |
13:50 | <Mathieu Hofman> | Can you say why? |
13:53 | <littledan> | So, are you upset about scheduler.yield(), which does this? |
13:53 | <Mathieu Hofman> | Oh I thought the signals concern was about certain leaks that were fixable, not the general mechanism |
13:54 | <littledan> | My concern with Signals are about leaks of global mutable state. Whether they're fixable or not, I still don't know, but you and the champions did seem optimistic. Maybe there was a misunderstanding in the nature of the leak? |
13:58 | <Mathieu Hofman> | So, are you upset about scheduler.yield(), which does this? |
14:08 | <Andreu Botella> | As far as I can tell, incumbent realms are observable, but they shouldn't allow communication between any two parties |
14:11 | <Andreu Botella> | there is only some difference to observe if the two parties run in different realms, and it seems like when an ECMAScript function (that is, not a built-in) is called, its realm is the incumbent realm for that function and any built-in or host function it might call |
14:12 | <Andreu Botella> | but I'm no expert on this |
14:15 | <Andreu Botella> | oh wait, maybe if some script in realm A causes a script execution in realm B, realm A might be observable |
14:22 | <Mathieu Hofman> | So, are you upset about scheduler.yield(), which does this? |
14:25 | <Andreu Botella> | if you don't pass the priority or signal options in the option bag, they'll be preserved from the previous call to scheduler.yield in the same task, even through awaits |
14:26 | <Mathieu Hofman> | So it carries some internal state, but it still doesn't make it observable, right? |
14:27 | <Andreu Botella> | I think the "current" value of those properties are not exposed, but you can use signal to abort a yield and not continue the task |
14:28 | <Andreu Botella> | I guess that wouldn't count as communication between parties though |
14:29 | <Andreu Botella> | actually, the promise would reject if the signal is aborted, so I guess that is communication |
14:57 | <Mathieu Hofman> | Yeah I just saw that. That is indeed a way for the caller to ambiently communicate with the logic it spawned. I'm a little surprised they didn't go with an explicit context since retrofitting this ambient state to existing APIs like fetch seems inappropriate. |
15:00 | <Mathieu Hofman> | Aka as a developer I could be surprised by either the abort signal of my task not being respected by fetch called within my task's execution, or by an existing fetch all the sudden aborting not realizing it became part of a posted continued task. |
15:24 | <Andreu Botella> | I haven't looked enough at the details, but I think aborting the signal would only implicitly abort any "inherited" scheduler.yield() calls – implicit signal propagation for e.g. fetch is not in scope for that proposal |
16:19 | <littledan> | It is limited in scope but still observable. But this is "deniable" by blocking APIs which do this sharing (just like all the other DOM APIs that deal with the document) |
17:21 | <bakkot> | if you don't pass the |
17:21 | <bakkot> | it just makes programs harder to reason about |
17:21 | <bakkot> | much harder |
17:21 | <shu> | love2global |
17:23 | <bakkot> | I don't know how it is that we have managed to get so many people doing API design who do not instinctively recoil from adding new global state |
17:24 | <Michael Ficarra> | the developer funnel will always be largest at the top |
17:25 | <shu> | why can't a developer love global state earnestly |
17:28 | <bakkot> | random developers can do whatever they want, it's only the platform designers whose bad opinions the rest of us have to live with |
17:28 | <bakkot> | speaking as a platform designer |
17:28 | <ljharb> | we used to love it, but then someone mutated it and now we don't |
17:28 | <Michael Ficarra> | TIL that ClassHeritage has access to the inner immutable class name binding but not private names |
17:28 | <Michael Ficarra> | that is so weird |
17:29 | <global_lover> | yeah i'm a class hater now |
17:29 | <bakkot> | class name is outer to the { and so is ClassHeritage while private names are inner to the { |
17:30 | <Michael Ficarra> | @bakkot inner class name binding |
17:30 | <Michael Ficarra> | the immutable one |
17:30 | <bakkot> | I mean that the name occurs syntactically outside of the { |
17:30 | <bakkot> | so it makes sense that the binding lives outside of it also |
17:30 | <Michael Ficarra> | remember that classes create 2 bindings: an outer mutable one and an inner immutable one |
17:30 | <bakkot> | yes I know |
17:30 | <bakkot> | that does not contradict what I just aid |
17:31 | <bakkot> | syntactically both bindings are created by a single identifier, and that identifier is outside of the { |
17:31 | <littledan> | I don't know how it is that we have managed to get so many people doing API design who do not instinctively recoil from adding new global state |
17:31 | <Michael Ficarra> | I think the inner one should only be available inside the { |
17:33 | <bakkot> | The whole point of AsyncContext is that it's not global, but rather scoped in the appropriate way. And that's what scheduler.yield does. We just know, empirically, that if you ask everyone to put in the right priority, they will just forget a lot of the time. Just like if we asked them to explicitly pass around the Otel spanid/traceid. scheduler.yield , if I am doing some random stuff, and then I do library.doWork() , and the library does scheduler.yield({ its own config }) , now my future calls to scheduler.yield don't work how I expected. that's crazy. |
17:33 | <bakkot> | if I gave the library the context object then it is perfectly reasonable for it to see/affect it |
17:33 | <bakkot> | but for it to be global, that's very strange |
17:35 | <Jack Works> | remember that classes create 2 bindings: an outer mutable one and an inner immutable one |
17:35 | <bakkot> | if you wanted to avoid the problem where people forget to pass the right priority for scheduler , the obvious fix is to require you to construct a scheduler object (and explicitly specify the priority) and pass it around, instead of there being a global one |
17:36 | <bakkot> | and to avoid the problem of requiring other libraries to thread it around, that's what AsyncContext is for |
17:37 | <global_lover> | look, even the functional people reconstructed implicit globals with monads to mimic their power |
17:38 | <Andreu Botella> | IIUC, with AsyncContext you have to have a reference to the context in order to see or affect the state. it's not going to be affected by calling into random libraries. whereas with |
17:39 | <Andreu Botella> | but maybe my understanding is wrong, because while I was trying to explain how it worked, I realized that according to my understanding scheduler.yield shouldn't be implementable based on CPED |
17:40 | <Andreu Botella> | but of course it is implemented that way |
17:40 | <littledan> | For context: https://github.com/WICG/scheduling-apis/blob/main/explainers/yield-and-continuation.md |
17:49 | <bakkot> | that lists as an open question:
if I understand what this is saying correctly, using the original priority would be fine by me - it's only the "calling a library function can change the priority used by the rest of my code" part I'm objecting to here |
17:49 | <Mathieu Hofman> |
Is it? AsyncContext allows you to propagate some data related to a reference you own. The web instead creates an internal reference, and exposes way to access information related to that reference to anyone. scheduler.yield is effectively equivalent to a |
17:51 | <Mathieu Hofman> | IIUC, with AsyncContext you have to have a reference to the context in order to see or affect the state. it's not going to be affected by calling into random libraries. whereas with |
17:53 | <Andreu Botella> |
|
17:53 | <Andreu Botella> | so in an async function, only for the remainder of the function |
17:54 | <Andreu Botella> | it wouldn't affect an outer async function |
17:54 | <Andreu Botella> | I don't think |
17:54 | <littledan> | seems like scheduler.yield does the equivalent of enterWith, which AsyncContext currently omits |
17:56 | <bakkot> | it wouldn't affect an outer async function |
17:56 | <bakkot> | it's weird in other ways though - we don't normally treat function boundaries as special at all, except for like using |
18:01 | <Andreu Botella> | it's not the function boundary that is special, it's the promise jobs that are |
18:01 | <snek> | is it treating function boundaries as special or treating promises as special, and async functions use promises |
18:01 | <snek> | oh lol |
18:02 | <Andreu Botella> | when you return from an async function, its reaction will run in a different promise job |
18:02 | <Andreu Botella> | the .then() was called before it, and since AsyncContext preserves the context when .then() was called, the context inside the async function can't flow back to its caller |
18:04 | <bakkot> | ah, makes sense |
18:11 | <littledan> | so the only boundary here is AsyncContext.Variable.prototype.run(callback) -- that's when something changes. Everything else is just propagating the map (as AsyncContext.Snapshot.prototype.run does) |
18:11 | <littledan> | both work by callbacks, not treating function boundaries or Promises special |
18:12 | <littledan> | Promises are just one of the cases where the snapshot is propagated (as a constant, not a subtask or something) |
18:12 | <littledan> | there's no way to mutate any variable in place, only to "fork" with variable.run |
18:12 | <littledan> | this all was pretty core to the SES analysis that AsyncContext was kosher |
18:13 | <littledan> | some people are coming into the AsyncContext group and proposing other semantics which would permit more like this yield pattern. I've generally been pushing back on it. |
18:13 | <snek> | as mentioned in the other channel you can still put a mutable thing into an async variable |
18:23 | <Andreu Botella> | btw, Dan and I only realized that scheduler.yield used basically enterWith or these alternative semantics during this conversation |
18:24 | <Andreu Botella> | so if it doesn't make too much sense that we brought up that API in this context, that's why |