03:09 | <Bakkot> | there is not a formal process |
03:09 | <Bakkot> | that said, I'd suggest reaching out to the relevant people, including authors and champions, first |
03:09 | <Bakkot> | if nothing else to make sure you don't misrepresent their positions |
05:23 | <ljharb> | imo, definitely always ask first before you post about somebody. that said, if someone's championing a proposal they've kind of signed up for their name to be associated with it, so i'd be surprised if that was a problem |
05:38 | <ljharb> | did we want `eval?.(x)` to be considered a direct eval? https://github.com/tc39/test262/pull/2667 |
05:38 | <ljharb> | it seems like we could choose to make it indirect |
05:45 | <rkirsling> | pretty sure that's a spec bug |
05:48 | <rkirsling> | JSC/SM/V8 all have a direct eval and I understood the intention to be "act as if the ?. weren't there" |
05:48 | <ljharb> | that's what the test is testing |
05:48 | <ljharb> | i'm asking, should `eval?.()` be an indirect eval instead |
05:48 | <ljharb> | because we want less direct evals, not more |
05:49 | <rkirsling> | I always have trouble remembering the implication there |
05:49 | <rkirsling> | but the point is we're not assigning anything |
05:52 | <ljharb> | sure, but given that `x?.(...a)` arguably desugars to `do { const tmp = x; tmp === null | tmp === undefined ? tmp : tmp(...a) }`, that'd be an indirect eval :-D |
05:54 | <rkirsling> | lol lol |
05:54 | <rkirsling> | oops |
05:54 | <rkirsling> | just meant one lol |
05:56 | <rkirsling> | I don't think that reflects anyone's actual viewpoint though |
05:58 | <rkirsling> | the user viewpoint can just think of ?: or && and the AST viewpoint can just wrap the eval call in an optional node |
05:58 | <ljharb> | sure, i get the easy arguments to direct |
05:58 | <ljharb> | i'm just wondering if it's a missed opportunity to kill more direct eval calls |
06:10 | <leobalter> | I wish we could have avoided the optional call form. This has sailed. |
06:10 | <ljharb> | is it really a web compat issue to change tho? |
06:11 | <leobalter> | It seems reasonable to have it as an indirect eval. I can’t read the specs right now (no computer at Sunday night policy at home) |
06:11 | <leobalter> | What does the spec says today? |
06:12 | leobalter | is lazy |
06:15 | <ljharb> | oh i assume it says it's direct, from that test262 PR, let me check |
06:24 | <ljharb> | "direct eval" links to https://tc39.es/ecma262/#sec-function-calls-runtime-semantics-evaluation , but it deals with cover grammars and MemberExpression and i can't tell if `?.` is part of that |
06:31 | <rkirsling> | the test262 PR is saying it's indirect |
06:31 | <rkirsling> | that's why I said there's a spec bug |
06:31 | <ljharb> | ohhhh ok |
06:32 | <ljharb> | i misread the OP then |
06:32 | <ljharb> | indirect seems better to me |
06:32 | <rkirsling> | yeah but everybody shipped with the intent of direct is the point |
06:33 | <leobalter> | PR is saying that, but apparently engines disagree. I’ll check the spec tomorrow. |
06:33 | <rkirsling> | it's possible Claude did that intentionally and none of us knew what to look for, but |
06:33 | <ljharb> | i hope it's an engine bug tbh |
06:34 | <rkirsling> | I think it's a spec bug either way if everyone read it wrong |
06:34 | <ljharb> | fair |
06:39 | <rkirsling> | hmmmmmmmmm |
06:39 | <rkirsling> | https://github.com/tc39/proposal-optional-chaining/issues/21 |
06:40 | <ljharb> | looks like it's intentional |
06:42 | <rkirsling> | never heard of `(0,eval)(foo)` but if "going out of one's way for indirect eval" is a thing then I guess that'd give `eval?.()` a reason to exist beyond like, "it seemed weirder to ban it" (which was my understanding) |
06:43 | <ljharb> | new forms of direct eval are what should imo be avoided |
06:44 | <ljharb> | (`(0,x.y)()` is a common form in babel output for "don't bring the receiver along", fwiw) |
06:46 | <rkirsling> | ah cool |
06:46 | <rkirsling> | that's good to know |
06:51 | <ljharb> | i also often use it when i don't want nasty ES6 name inferencing breaking my anonymous function test cases |
06:52 | <rkirsling> | guess you'll have to apply that to logical assignment too then |
06:52 | <rkirsling> | that's too bad |
06:52 | <ljharb> | yup |
17:25 | <howdoi> | Bakkot: roger that! |
17:27 | <leobalter> | ljharb rkirsling: ok, reading specs ... I believe it's indirect. I can stress it out with step by step, not to long but can be boring |
17:28 | <ljharb> | thanks, i'd love to understand if you have the time :-) |
17:47 | <leobalter> | I'm writing it all down |
17:54 | <leobalter> | ljharb: rkirsling https://gist.github.com/leobalter/0b32f73470b134617bed170492cd2089 |
17:55 | <leobalter> | please correct me if I'm wrong. It's Monday morning :) |
17:59 | <devsnek> | wouldn't be a monday morning without some discussion of indirect eval |
18:01 | <ljharb> | leobalter: i'm not clear on how you get from EvaluateCall to eval() |
18:03 | <devsnek> | ljharb: Call() calls eval |
18:03 | <devsnek> | the actual %eval% |
18:05 | <ljharb> | where does the fourth arg to `PerformEval` come from? |
18:05 | <devsnek> | https://tc39.es/ecma262/#sec-eval-x |
18:07 | <leobalter> | EvaluateCall, step 7, the Call will defer to eval, as devsnek mentioned. I just skipped that part but you can go to Call, then F.[[Call]] and https://tc39.es/ecma262/#sec-built-in-function-objects-call-thisargument-argumentslist |
18:11 | <ljharb> | ohh i see |
18:11 | <ljharb> | thanks |
18:11 | <ljharb> | makes sense to me |
18:11 | <devsnek> | https://gc.gy/60554244.png |
18:11 | <devsnek> | classic case of having too much abstraction |
18:45 | <devsnek> | doesn't this mean that babel/ts transforms for optional chaining are incorrect for eval?.() |
18:59 | <shu> | i feel like eval?.() should not be indirect eval |
19:01 | <ljharb> | why not |
19:01 | <ljharb> | ? |
19:06 | <ljharb> | i can see the argument both ways, ofc, but it seems like when either answer makes sense we should pick the one that avoids the bad thing |
19:07 | <shu> | because the original motivation was analyzability, and a global `eval?.()` is perfectly analyzable. and a pretty weak simplicity argument: less stuff to remember if the property is "non-member calls to e-v-a-l" instead of "non-member non-optional calls to e-v-a-l-(" |
19:08 | <shu> | the second argument being pretty weak since maybe for some folks just remembering "e-v-a-l-(" is the easier thing |
19:08 | <ljharb> | interesting, i didn't know the original motivation |
19:08 | <shu> | ljharb: it is THE backwards breaking change. without saying indirect eval didn't have access to the local bindings, i honestly think JS wouldn't have taken off the way it has |
19:09 | <ljharb> | i mean, i think i get why indirect eval matters |
19:09 | <ljharb> | i didn't think about it in terms of analyzability |
19:11 | <shu> | yeah we wouldn't have been able to put bindings on-stack otherwise |
19:12 | <devsnek> | direct eval is big deopt |
19:12 | <leobalter> | shu: in this case we need a normative change. I'm not opposed to any direction we pick. I'd prefer consistency with what web developers expect. And as a web dev, I see `eval?.(x)` as `eval ?? eval(x)`, in the direct form. That's totally debatable. |
19:13 | <shu> | leobalter: yeah, i agree we should err towards minimizing surprises for this one |
19:13 | <shu> | there're no good first-principles arguments |
19:13 | <devsnek> | i think the whole direct/indirect thing is weird enough that there is no useful rule to follow |
19:13 | <ljharb> | right, i think we can revisionist history to justify either decision equally |
19:14 | <devsnek> | https://gc.gy/60558260.png |
19:15 | <rkirsling> | PRNG has spoken |
19:21 | <rkirsling> | but yeah I don't want to implement this unless we're sure |
19:21 | <rkirsling> | seeing as we have consistency in what's shipping right now |
19:21 | <rkirsling> | it's a little weird to me that SM moved so quickly |
19:21 | <leobalter> | I wonder who is relying on eval today (they do exist, right?) and what they really expect. I'd find odd of anyone relying on the very specific `eval?.(x)` case. The test came in after an implementation for ESLint, so I don't think they care if it's direct eval or not other than for spec consistency. |
19:24 | <jridgewell> | I'm fine with whichever we choose |
19:24 | <jridgewell> | Side note: `eval ?? eval(x)` is incorrect, because it won't eval the RHS |
19:25 | <jridgewell> | I think we should add interrobang operator `!?` to do "RHS if LHS is non-nullish" |
19:29 | <rkirsling> | I can understand an argument for utility if `eval?.()` is easier than `(0,eval)()` but I don't think that implies that it's intuitive |
19:29 | <rkirsling> | it's kind of a fancy hack |
19:30 | <shu> | yeah i guess i don't really care |
19:30 | <jridgewell> | I think the easier option is to make it a direct eval |
19:31 | <jridgewell> | Because every implementation (except engine262) and transpiler uses direct eval |
19:32 | <jridgewell> | Ooo, XS does indirect, too. |
19:32 | <rkirsling> | yeah I think the reason we all agreed on it was because we were all understanding that the behavior of ?. is meant to be just the behavior without it, when the LHS is non-nullish |
19:32 | <jridgewell> | That makes sense to me. |
19:33 | <leobalter> | that was me trying to be smart instead of using && |
19:34 | <ljharb> | jridgewell: wait, how is that incorrect |
19:34 | <ljharb> | jridgewell: `x?.(y)` is like `x ?? x(y)`, no? including the short-circuiting? |
19:34 | <ljharb> | (just not including the double evaluation of x) |
19:35 | <rkirsling> | no there's no call if you're satisfied with `x` |
19:35 | <jridgewell> | `x ?? y` is `x == null ? y : x` |
19:35 | <jridgewell> | `x?.()` is `x == null ? undefined : x()` |
19:36 | <jridgewell> | So `x ?? x()` is `x == null ? x() : x` |
19:36 | <jridgewell> | We invoke a `null`/`undefined` |
19:36 | <ljharb> | oh right, the opposite |
19:36 | <jridgewell> | Yah |
19:37 | <ljharb> | so it'd be like `x ‽ x(y)` i guess |
19:37 | <jridgewell> | So, interrobang does a not-nullish check, instead of a nullish check. |
19:37 | <ljharb> | right |
19:37 | <rkirsling> | or && |
19:37 | <ljharb> | would it be !? or ?! tho |
19:37 | <jridgewell> | I like it because interrobangs should be a thing |
19:37 | <rkirsling> | ?. is a more precise &&, ?? is a more precise || |
19:37 | <jridgewell> | And `!?` reads "not nullish" |
19:38 | <ljharb> | would you then expect `a!?.b` even tho that be silly since it'd always throw? |
19:38 | <ljharb> | or only as a binary operator |
19:39 | <jridgewell> | > ?. is a more precise && |
19:39 | <jridgewell> | Only if you mean property access. Doesn't work well for root value |
19:39 | <jridgewell> | Only the binary operator, `!?.` makes no sense (and is already valid-but-bad typescript code) |
19:39 | <ljharb> | right |
19:39 | <rkirsling> | but `??` can be read as "falls back to" and to `?.` covers the `&&` guarding usage so |
19:40 | <jridgewell> | Only if you're doing property access |
19:40 | <rkirsling> | I don't think this `!?` would actually be very useful...I think it could really confuse wrt ?. |
19:40 | <jridgewell> | Babel had a case with private optional |
19:41 | <jridgewell> | Where we do `obj == null : undefined : getPrivate(obj)` |
19:41 | <jridgewell> | And that can't be represented with either `?.` nor `??`. |
19:41 | <jridgewell> | We need interrobang to do it. |
19:42 | <jridgewell> | So, `??` is stricter `||` and `!?` is stricter `&&`. |
19:42 | <ljharb> | i don't think ?? is a stricter || |
19:42 | <ljharb> | stricter || would be "true or false" instead of "truthy or falsy" |
19:42 | <jridgewell> | And `?.` is a special case of `&&` for property access/call |
19:43 | <rkirsling> | I'm just saying, this whole tangent started because of a genuine mistake where !? wouldn't have actually been appropriate |
19:44 | <rkirsling> | and viewing !? as a solution is dangerous if it's going to confuse people that should be using ?. |
19:44 | <jridgewell> | `!?` is the appropriate solution, though. |
19:45 | <jridgewell> | `eval !? eval('test')` |
19:45 | <rkirsling> | but that code should never be written |
19:46 | <jridgewell> | The Babel case needed it, too. |
19:47 | <rkirsling> | sure I get that you already came up with it previously for that reason, yeah |
20:19 | <rkirsling> | seems like the case you gave could be handled _inside_ the function instead though, and while `!?.` would be nonsensical, `!?=` would be expected, yet of questionable utility |
20:19 | <rkirsling> | the issue is that with || and &&, truthy and falsy are on equal footing |
20:20 | <rkirsling> | nullish can never be on equal footing with non-nullish because if we could go back and do things over again, we would just have null and not nullish |
20:23 | <rkirsling> | so even though ?? and ?. bear similarity with these, there's a baked-in notion of "falling back" since it's all about convenient null-guarding and not about two similar buckets |
21:06 | <littledan> | Hey everyone, let's try to keep technical discussion in #tc39 when possible, so it's publicly accessible |
21:07 | <jridgewell> | But this is publicly accessible? |
21:07 | <ljharb> | littledan: this channel is publicly accessible already |
21:07 | <ljharb> | the only place we can't have technical discussions is the reflector. |
21:08 | <littledan> | I'm not talking about in terms of policy requirements, but we have more non-delegates hanging out in #tc39, since we advertise the existence of #tc39 publicly |
21:08 | <littledan> | (sorry, my comment was imprecise) |
21:08 | <ljharb> | sure. but sometimes we don't want to have the discussion with non-delegates, and that's what this channel is for |
21:08 | <littledan> | ok |
21:09 | <rkirsling> | sometimes it's just inertia over where a topic started |
21:09 | <rkirsling> | or like |
21:10 | <rkirsling> | if there's multiple topics then our channel count is effectively our thread count |
21:10 | <rkirsling> | hehe |
22:20 | <rickbutton> | spec question: is it valid to use a spec-List as a `this` argument to a function `Call`? (specifically, the `this` argument of the `adder` in a usage of `AddEntriesFromIterable`. it is mentioned that spec-types should not be used as object properties or variable values, but does that also apply to `this`? |
23:21 | <ljharb> | rickbutton: it's not valid |
23:21 | <ljharb> | rickbutton: spec values can't be exposed to user code |
23:22 | <ljharb> | rickbutton: in the AddEntriesToIterable case, you could argue that it's not exposed, but that'd be an editorial question the editors haven't previously had to consider |
23:35 | <rickbutton> | makes sense. |
23:37 | <rickbutton> | I don't think I will be able to re-use AddEntriesFromIterable in that case, will have to create a new op. either way its a small amount of spec |