| 01:12 | <rkirsling> | wait for real? https://github.com/tc39/test262/blob/main/test/built-ins/Error/cause_property.js#L37 |
| 01:14 | <rkirsling> | why would there be a difference between explicit `undefined` and non-existence in a options bag? |
| 01:15 | <ljharb> | rkirsling: differentiates between an error with no cause and one with a cause of `undefined`? |
| 01:16 | <ljharb> | perhaps, a “undefined is not a function” error :-p |
| 01:16 | <rkirsling> | lol |
| 01:16 | <rkirsling> | but it's not null |
| 01:16 | <rkirsling> | like, is there any precedent for that anywhere in the language? |
| 01:17 | <ljharb> | for presence and undefined being different? tons. it’s the first options bag tho |
| 01:18 | <rkirsling> | that's the thing |
| 01:18 | <ljharb> | (non-intl, at least) |
| 01:18 | <ljharb> | cause, and temporal, and growable array buffer, and import assertions, might be the first options bags, whichever lands first? |
| 01:19 | <ljharb> | but the way userland treats options bags does often pívot on presence and not undefined, i think |
| 01:19 | <rkirsling> | that would be shocking to me as a user |
| 01:20 | <rkirsling> | ...assuming it resulted in anything noticeable |
| 01:20 | <ljharb> | i mean, in this case if you don’t differentiate between absent and undefined, error.cause will be undefined either way, so I’m not sure why it makes a difference :-) |
| 01:21 | <rkirsling> | because an implementation has to create a property descriptor for no reason whatsoever |
| 01:21 | <ljharb> | oh, you’re not talking about “it’s absent when absent” you’re surprised it’s present when undefined? |
| 01:22 | <ljharb> | the Get is observable |
| 01:22 | <rkirsling> | yeah like that's a userland object that may have been reused |
| 01:22 | <ljharb> | it seems strange to me to do the Get and not make the property |
| 01:22 | <rkirsling> | why would setting to undefined not be equivalent to deletion |
| 01:22 | <rkirsling> | from the API's standpoint |
| 01:22 | <ljharb> | because it’s not, for many things? |
| 01:23 | <ljharb> | anything using Object.keys/entries/values, or object spread/rest, will see the key if it’s present and undefined, but won’t if it’s absent |
| 01:24 | <rkirsling> | I mean yeah cases of enumeration are supposed to be the time where it makes a difference |
| 01:24 | <ljharb> | typically you don’t mutate objects either; you make a new one that lacks the property you don’t want |
| 01:24 | <ljharb> | I’m not sure why someone would mutate but not delete |
| 01:24 | <rkirsling> | this isn't enumerating anything, it's reaching in and taking action on undefined |
| 01:24 | <rkirsling> | I mean that was the old norm |
| 01:24 | <ljharb> | i don’t think it was |
| 01:24 | <rkirsling> | it absolutely was |
| 01:24 | <ljharb> | the old norm was delete. Then it shifted to “copy” |
| 01:25 | <rkirsling> | we have a whole webkit blog post about "hey it's no longer unperformant to use delete" |
| 01:25 | <ljharb> | the period of time where deletion was thought of as bad and mutation was also not thought of as bad is either very short or nonexistent |
| 01:25 | <ljharb> | well sure. But long before that came out, mutation fell out of vogue |
| 01:25 | <ljharb> | that there’s old code that’s now faster is great, but doesn’t affect user expectations imo |
| 01:26 | <rkirsling> | okay fine but it doesn't have to be a matter of reuse |
| 01:26 | <ljharb> | iow, that optimization was years too late to actually cause users to like using `delete` |
| 01:27 | <ljharb> | (here I’m arguing against “setting to undefined as deletion” as a justification; unrelated to whether we care about presence vs undefined or not) |
| 01:28 | <rkirsling> | if you were simply filtering existing options then you're not expecting setting undefined to do anything |
| 01:29 | <rkirsling> | `{ foo: originalOptions.foo }` or something |
| 01:29 | <ljharb> | true. but I’d expect you to spread the original object, and overwrite what you wanted, rather than doing it one at a time |
| 01:29 | <ljharb> | either way tho, having an own undefined cause property, versus lacking one - when do you expect that to cause a problem for users doing this? |
| 01:30 | <rkirsling> | I don't, I just see no benefit to having a property descriptor get created |
| 01:31 | <rkirsling> | if there's not a precedent for it then it seems like taking conscious action where none was desired |
| 01:31 | <ljharb> | right, so for these users, no difference - but for users who do care, the presence or absence of a property descriptor actually communicates something useful. |
| 01:33 | <ljharb> | unfortunately since we decided not to do an internal slot and a prototype accessor, this kind of choice comes up, but i think matching own-ness between the options bag and the error instance is actually a useful property. |
| 01:33 | <rkirsling> | it shouldn't? |
| 01:33 | <rkirsling> | like, I would call it harmful if it's viewed as not extraneous but usable |
| 01:33 | <rkirsling> | it's undefined not null |
| 01:34 | <ljharb> | why is that different |
| 01:34 | <ljharb> | Both are explicit values |
| 01:34 | <ljharb> | the belief that one means “explicitly empty” and one means “never set” is decidedly not a universal one (optional chaining and nullish coalescing operate on both, because of this) |
| 01:35 | <rkirsling> | how are you seeing optional chaining and nullish coalescing as supporting your point |
| 01:37 | <rkirsling> | it is not expected for mere existence to affect anything but enumeration |
| 01:37 | <rkirsling> | a user has no other way to opt out using an object literal |
| 01:39 | <rkirsling> | anyway I am completely exhausted and I didn't come here to debate minutiae; it genuinely seems like a bug, if there's no precedent to say otherwise |
| 01:39 | <rkirsling> | people code in different ways |
| 01:39 | <rkirsling> | this is not expected behavior |
| 01:41 | <ljharb> | i hear that you don't expect it |
| 01:41 | <ljharb> | i very much do |
| 01:42 | <ljharb> | i write code all the time that checks hasOwnProperty |
| 01:48 | <rkirsling> | was this ever pointed out in committee? |
| 01:48 | <rkirsling> | like, if this is going to be the thing to set the precedent then having such a conversation seems like a stage 4 blocker |
| 01:50 | <ljharb> | hm, not sure. If we didn’t explicitly decide it tho then i don’t think it could be a binding precedent really. I’ll try and find something in the notes and repo tho |
| 02:02 | <Bakkot> | https://github.com/tc39/proposal-error-cause/pull/26#discussion_r583989100 |
| 02:02 | <Bakkot> | rkirsling ljharb ^ |
| 02:03 | <Bakkot> | and also https://github.com/tc39/proposal-error-cause/issues/2#issuecomment-789375512 |
| 02:07 | <rkirsling> | wow |
| 02:07 | <rkirsling> | that is fascinatingly weird |
| 02:09 | <rkirsling> | but that rationale combined with the knowledge that shu also brought this up makes me somewhat less queasy |
| 02:10 | <rkirsling> | Bakkot: are you concerned with this setting a precedent for options bag options in general though, even though the motivation here has nothing to do with that? |
| 02:10 | <Bakkot> | no, not especially |
| 02:10 | <rkirsling> | okay |
| 02:12 | <Bakkot> | like temporal has a bunch of options-bag-like cases and they all just do `get` |
| 02:12 | <Bakkot> | e.g. |
| 02:13 | <Bakkot> | which is fine, because they are not in the situation that "undefined" is a sensible value for the option to take plus also having different behavior for presence and absence of the option |
| 02:13 | <Bakkot> | that's a fairly unique scenario |
| 02:13 | <Bakkot> | I do want it to serve as precedent for options bags which meet both of those criteria, because I think it's the right behavior there |
| 02:19 | <rkirsling> | hmm |
| 02:19 | <rkirsling> | can you envision another (hypothetical) example? |
| 02:22 | <Bakkot> | not off the top of my head |
| 02:22 | <Bakkot> | most places are at least a little bit typed |
| 02:23 | <Bakkot> | the only reason this one isn't is because it's intended for use with existing untyped syntax |
| 02:23 | <Bakkot> | (namely throw/catch) |
| 02:32 | <rkirsling> | fair enough |
| 02:32 | <ljharb> | Bakkot: thanks for finding that |
| 02:34 | <ljharb> | i do agree that when `undefined` isn't a reasonable real value for an option - which will be almost every other case - then it should just do a Get |
| 15:17 | <shu> | perhaps we should introduce a 3rd nullish type to mean absent |
| 15:17 | <shu> | we can even name it *empty* so it's easily confused with ~empty~ in the spec |
| 15:18 | <ljharb> | `nil` |
| 15:19 | <shu> | will there be a separate concept of "nilish" vs "nullish"? |
| 15:19 | <shu> | then we can extend the JS trinity diagram |
| 15:21 | <ljharb> | i am reminded of https://github.com/BrendanEich/nil (which brendan resurrected but didn't create) |
| 15:22 | <shu> | oh my goodness, that toString semantics |
| 17:17 | <robpalme> | could I get a blessing here plz https://github.com/tc39/notes/pull/123 |
| 17:53 | <akirose> | robpalme: i have edits. should I open a PR on your fork's branch? |
| 17:53 | <akirose> | ljharb: don't merge yet! i mean, you can, but 👆 |
| 17:55 | <ljharb> | nah was just stamping per request |
| 17:55 | <ljharb> | i'll let yall handle it |
| 19:39 | <robpalme> | I won't merge. Please use suggestions on the PR. |