05:35
<bakkot>

stray thought: we've long said that the ratio of readers to writers of code is high, so we should prioritize code being easy to understand over code being easy to write. LLMs make that dramatically more true: for a lot of code the ratio is now literally infinite, i.e., there is no one who wrote it, and just people who are reading it. so it is more important than ever that code be easy to understand.

this is to say, I am increasingly convinced that if (let x = ...) { } else { x } should be an early error, so that this code cannot be written, and thus the only cases that any reader of code will ever encounter are completely unambiguous.

11:30
<rkirsling>
even as somebody vehemently opposed to LLMs I agree with your conclusion :)
15:13
<Michael Ficarra>
the original sin was allowing people to write 0.1 instead of its real value 0.1000000000000000055511151231257827021181583404541015625 and pretend like it's 0.1
15:14
<nicolo-ribaudo>
Why don't we start writing a lint rule for that
15:14
<Justin Ridgewell>

Doesn't this mean the AI's naive assumption is the most natural? I see this and assume the x is available within both.

I think what's increasingly confusing is only using's scope. Just ban it. If you want to auto dispose, do it within the if body. if (let x = …) { using _ = x; … } else { x }.

15:14
<nicolo-ribaudo>
With autofix
15:14
<Michael Ficarra>
I literally wrote one for eslint like 10 years ago
15:14
<Michael Ficarra>
it was rejected
15:14
<nicolo-ribaudo>
:/
15:15
<peetk>
but what if you want to condition on something on the disposable
15:15
<Michael Ficarra>
@nicolo-ribaudo https://github.com/eslint/eslint/issues/8447
15:15
<Justin Ridgewell>
The disposable is x. if (let x = …; x.isOpen()) {}
15:16
<bakkot>
... no? Nothing I wrote had anything to do with the AI's assumption. Also I think you missed my point: some people think it will mean one thing, some people another. We can argue about which is "most natural", but if you simply don't ever encounter an x in the else then it doesn't matter which assumption you'd make.
15:16
<Michael Ficarra>
it was my last attempt to contribute to eslint lol https://github.com/eslint/eslint/issues?q=sort%3Aupdated-desc%20is%3Aissue%20state%3Aclosed%20author%3Amichaelficarra
15:17
<peetk>
oh i see. i think that's (a lot) worse than having to do { let x = ...; if ... else ... } in the cases where you want x in the else
15:18
<Justin Ridgewell>
The AI wrote x because it assumes it's available, which I share when reading it.
15:18
<Justin Ridgewell>
Isn't that the whole point of this proposal?
15:20
<peetk>
bakkot has several times made the argument that without the using, this proposal is maybe not even worth it given how easy it is to desugar. no else + using gives this proposal a very strong use case where it's not so easy to desugar yourself.
15:22
<Justin Ridgewell>
I think using is the source of its worse semantics.
15:22
<Michael Ficarra>
The AI may have written x to intentionally read from an outer x because it knows how the feature works.
15:22
<bakkot>
Maybe it was trying to refer to the x from the outer scope; you can't tell just from that snippet. But who cares? It will immediately discover that this doesn't work and write something else. (Or, much more likely, if we ban it then it will already be aware that it is not legal and will not write it in the first place.) And so by the time a human actually reads it the human will not see the problematic construct and so will not encounter a situation where their intuition may differ from the AI's intent.
15:23
<peetk>
can you elaborate on this
15:23
<bakkot>
My point is, reasoning from "what is more natural" doesn't work all that well, because clearly people have different intuitions, but by simply banning it people never need to be exposed to the fact that their intuitions differ in the first place (except in the increasingly-rare case that they're attempting to write the code, not just read it, and are not fully aware of how it works)
15:24
<bakkot>
I feel like you are not engaging with this argument at all
15:24
<Justin Ridgewell>
^ this entire discussion. x should be available within the else {}. The only justification for it not, is that we want the using scope to end with the if's .
15:24
<Justin Ridgewell>
If there is no disposal, there's no need to remove its binding from the scope.
15:25
<bakkot>
No! there is another excellent justification for it to be an error, which is that some people assume it wouldn't be available (i.e., would read through to the outer scope), and that makes it inevitably confusing for a lot of people to read
15:25
<peetk>
can you explain why you want this proposal? is saving 5 keystrokes worth it?
15:26
<Justin Ridgewell>
Based on the need for using to dispose? This isn't an issue with without it, who would care if the x is available or not?
15:26
<Justin Ridgewell>
It's increasingly not worth it.
15:27
<ljharb>
i mean, if it were available in the else, i'd use it to determine what kind of falsy value it was, no using involved
15:27
<peetk>
but you could do that without this proposal very easily
15:27
<ljharb>
sure, by defining it outside the if
15:28
<Justin Ridgewell>
That's exactly my reasoning. if (let x = …; x.isFile()) { } else if (x.isDir()) {} else {}.
15:28
<James M Snell>
at this point, I'm rather leaning towards not adding this because of the complexities and issues raised that seem to outweigh the syntactic benefit
15:28
<peetk>
yea but again why not just pop that let x outside the if
15:28
<Michael Ficarra>
@Justin Ridgewell there's no use in defining that in the if conditional though
15:28
<bakkot>
no, not based on the need for using to dispose, just based on literally looking at it and/or familiarity with languages like Rust and Swift
15:29
<bakkot>
did you see Lea's polls? there lots of people with this intuition https://github.com/LeaVerou/blog/discussions/139
15:29
<bakkot>
you can say their intuition is wrong, which, fine, whatever, they are wrong and bad. but we can still avoid making their lives harder, at very low cost, simply by making it an error
15:29
<Justin Ridgewell>
I am looking at it? It should be available within else.
15:29
<bakkot>
yes
15:29
<bakkot>
I recognize that this is your intuition
15:30
<bakkot>
I would like you to acknowledge that other people have a different intuition
15:30
<bakkot>
not based on reasoning from using
15:31
<Justin Ridgewell>
I agree. This proposal only opens a confusing core case, with the goal of making using nicer in an if. I don't think that's worth it.
15:32
<Justin Ridgewell>
I do. But if they don't want to use the x, why would the write it in the else? Why would it ever refer to an outer scope when there's a inner scope that defines it?
15:33
<bakkot>
I am concerned primarily about people reading code, not people writing code
15:34
<bakkot>
so they probably wouldn't! but if other people or AIs do, because they share your intuition or have learned that it works that way, they will encounter that, and be confused
15:34
<Justin Ridgewell>

Why would it ever refer to an outer scope when there's a inner scope that defines it?

I think that applies whether you're reading or writing.

15:34
<bakkot>
if it is not legal to write, then no, it does not apply
15:35
<Michael Ficarra>
isn't it a catch-22? won't more devs use it as a primitive?
15:35
<rkirsling>
if a new binding were meant to apply in an else then it would be presumably defined next to the else, just like catch
15:35
<ljharb>
also bigint was never going to be used by double digits of devs, it was designed intentionally not to be. if it was "Integer", however, it would have been
15:35
<nicolo-ribaudo>
Yeah I think if it was a primitive devs would start defaulting to decimals
15:36
<nicolo-ribaudo>
Like they default to let instead of var, even if var is faster
15:36
<Michael Ficarra>
I agree with ACE, I would love a primitive, but an API is good enough as long as our path to an eventual primitive is clear.
15:37
<Richard Gibson>
is this a claim that the spelling of built-in function BigInt made the difference? Which isn't even encountered with n-suffix syntax?
15:37
<ljharb>
no, the spelling reflects the semantics, which is what made the difference. the lack of free coercion between integer numbers and BigInt is related.
15:37
<Caio Lima>
In almost all languages I mentioned, Decimal is exposed through an Object-like way.
15:37
<eemeli>
@aclaymore:matrix.org My question from the end of the queue: What benefit would you get from a spec-defined object Decimal?
15:38
<Ashley Claymore>
Because having a standard is very useful. Is that not why we are here :D
15:38
<nicolo-ribaudo>
That libraries you depend on know how to deal with it rather than having to convert my decimal type to something that those libraries understand
15:39
<Richard Gibson>
each bigint literally is an arbitrary-precision integer number
15:39
<Michael Ficarra>
people always forget about the value of data interchange!
15:39
<Richard Gibson>
oh, you mean integral Number... n/m
15:40
<eemeli>
Isn't that problem solved by Amount?
15:40
<ljharb>
yes. and, you can't use bigints interchangeably in places that expect integer numbers.
15:40
<Caio Lima>
keith_miller: I think your question on queue were related to this, right?
15:40
<ljharb>
if bigint was designed differently, Number would be ~only~ primarily used for non-integers today in new code.
15:42
<keith_miller>
Would it? I'm not convinced. It'd probably be much slower unless BigInt were actually int32_t
15:42
<ljharb>
most people don't care about performance in that way ime.
15:43
<Richard Gibson>
I would expect int64 fast-paths
15:43
<keith_miller>
You'll never see int64 fast-paths because there's not enough bits in registers
15:44
<keith_miller>
Until we have 128 bit registers
15:44
<ljharb>
int32 fast paths then
15:44
<Olivier Flückiger>
that is exactly the problem.... that there will be this expectation...
15:44
<Olivier Flückiger>
without a guarantee
15:44
<Richard Gibson>
isn't this contradicted by the other languages that went that way?
15:44
<keith_miller>
Still not going to be any faster than a double unless it wraps around on overflow
15:44
<ljharb>
it doesn't have to be faster
15:45
<Caio Lima>
Well, one of the motivations of adding Decimal to the language is that it's being designed in a way that it can be potentially more performant than user-space Decimals. One point regarding data transportation here is that if there's a Decimal into the language, you can think in applications written in WASM where the transportation is essentially a pointer exchange.
15:45
<keith_miller>
They have static types so they don't have to do boxing
15:45
<ljharb>
sure, that also applies to arbitrary integers/bigint tho
15:47
<keith_miller>
That's probably not going to happen. At the wasm layer it's going to be an externref. Wasm doesn't and probably will not in the foreseeable future have BigInts.
15:47
<ljharb>
does that mean it wouldn't happen with decimal either?
15:47
<Richard Gibson>
Python types are not static
15:50
<keith_miller>
AFAIK, Python does not inline arbitrary 64-bit integers even on 64-bit platforms
15:51
<keith_miller>
By pigeon hole principle it's just not possible
15:55
<bakkot>
this would be a reason not to do it; adding something which people actually use a lot but which is slower than the thing they'd otherwise do is worse for the world than adding one that people don't use a lot
15:56
<ljharb>
although i think way more people would use a decimal primitive, i think enough would use a decimal object that your statement applies to both
15:57
<bakkot>
if people start using decimal for things that don't need it, that would be bad, yes
15:57
<bakkot>
however I am more optimistic; languages like Java have decimal and people only use it in places that actually warrant one, IME
15:58
<bakkot>
(well, most people only use it in places that warrant. there's always someone.)
15:58
<eemeli>
Queue is stuck?
15:59
<Michael Ficarra>
chairs are sleeping 🛌
15:59
<peetk>
ljharb can you elaborate on this: "I don't think that the object-based API solves it and I think it needs to be a primitive in order to solve it" (pulled from the notes so may not be word perfect) what aspect of the problem statement isn't solved by the object-based API?
16:00
<ljharb>
oh i'm sure the problem statement is currently carefully worded so the object ones technically solves it, but i see the problem statement as broad enough that it doesn't
16:00
<Caio Lima>
Regarding this question, module reasons we already talked, why do other languages provide a Decimal into their language? From Python's PEP (https://peps.python.org/pep-0327/) the motivation started to be a building block to implement Money, but they also find that it would be beneficial to have that type for other use-cases as well. I see that interactions with DB records having a Decimal type is quite valuable for applications where JS is being widely used (mostly server-side JS here). Would Amount be capable of interacting with this DB data interaction case, for example?
16:01
<ljharb>
math and numbers is too important and too basic to be objects.
16:01
<Michael Ficarra>
"math is basic" -- Jordan Harband, Fields medalist
16:01
<ljharb>
basic !== easy :-p
16:01
<peetk>
what do you see as the problem statement?
16:02
<Ashley Claymore>
Objects if anything can be more powerful than primitives
16:02
<ljharb>
i want to do normal math. with operators. by typing mostly numbers.
16:02
<Ashley Claymore>
Numbers are not being removed
16:03
<ljharb>
Number isn't normal math
16:03
<Ashley Claymore>
base10 is no more normal than base2 in maths
16:03
<peetk>
isn't that just a distinct problem?
16:03
<ljharb>
tell that to my fingers :-)
16:04
<bakkot>
if you are thinking of this as "I want to use it in in everyday life for normal things that I currently use Numbers for", we should not add it because it would be very bad if people start doing that
16:04
<ljharb>
why?
16:04
<bakkot>
because it will be much slower
16:04
<ljharb>
that is absolutely the long term goal i want
16:04
<bakkot>
then we should absolutely not do this
16:04
<Ashley Claymore>
Some of us care about performance :D
16:04
<Michael Ficarra>
lots of machines have decimal128 instructions!
16:05
<ljharb>
i fail to understand why, for current common Number usages, it'd be meaningfully slower
16:05
<Ashley Claymore>
because we have to box in JS
16:05
<Caio Lima>
But this is mostly about expressivity and syntax ergonomics than the problem being solved
16:05
<peetk>
Bloomberg has a clear need we've articulated and we are content that the Object API would solve it. you're objecting because you have a different problem that this doesn't solve? is that correct?
16:05
<ljharb>
if it's something i can run a trivial transpilation over and make fast, then browsers can just do that
16:05
<shu>
oh yeah like what
16:06
<ljharb>
i also think they're aspects of the same problem. the performance motivation was just contradicted above, so what more remains to prefer "not a library"?
16:06
<Caio Lima>
There's lack of hardware to use decimals instead of Numbers.
16:06
<Michael Ficarra>
some!
16:06
<ljharb>
yes, and those are important concerns
16:06
<shu>
uh huh
16:06
<ljharb>
sounds like that's the problem that needs solving then
16:06
<peetk>
there is value in standardizing on a format
16:06
<bakkot>
no, to be clear, native can be more performant than a library, just not as performant as a Number
16:07
<ljharb>
sure. but we generally don't add something to the language merely to have a coherence point
16:07
<bakkot>
performance is a totally valid reason to do this if, and only if, it is replacing libraries and not Numbers
16:07
<ljharb>
since a library can be written in wasm, you're saying native would still be faster than that? (like, guaranteed)
16:08
<peetk>
i would argue that this is a good enough reason on its own. this is a standards body!
16:08
<peetk>
if it were in the language you could trust that every library will handle them
16:08
<ljharb>
standards bodies don't exist just to churn out standards for standardization's sake tho (ideally)
16:09
<peetk>
do you think we're doing this for fun
16:09
<bakkot>
depends on the hardware and whether wasm ever gets native decimal operators and how smart the JIT is, so no I can't guarantee it, but typically yes.
16:11
<keith_miller>
Devs can do it and send it to browsers :)
16:11
<Caio Lima>
But still I have the question, why is it not common that Decimals aren't primitives on most of the languages that provides them in their STL? There should be a reason for that common design choice.
16:12
<ljharb>
why would that be better, to push the cost to servers that won't all do it rather than making the entire web faster by doing it in-browser?
16:13
<ljharb>
i can't speak to other languages, but JS is unique in many ways, and we have a lot of idioms that are "weird" when attempted in other languages.
16:13
<shu>
you think it's faster for the entire web to do something every page instead of once ahead of time?
16:13
<keith_miller>
It would be faster on the server
16:13
<ljharb>
touché
16:13
<Caio Lima>
From my studies, the reason is rooted on the case that Decimal should have their specific usage, while Nubmer (float64) is preferred for everything else.
16:15
<ljharb>
"should" is pretty load-bearing there
16:15
<ljharb>
certainly the performance/hardware argument holds, for the current time
16:15
<ljharb>
but in the fullness of time we'll have hardware where it no longer does. at that point why "should"?
16:15
<Caio Lima>
That's why I asked if "does the use cases we have are compelling enough?" If we think the usage is too niche, I'd like to hear a block concern on that.
16:15
<bakkot>
is "bushel" an actual unit or just an example of a thing you could have an Amount of but that is probably not a real unit
16:16
<ljharb>
that's what i've been saying the entire life of this proposal - that if it's not going to be able to replace Number, its use case is imo too niche to add to the language
16:16
<dminor>
There's four pecks in a bushel
16:16
<ljharb>

In the U.S., it is legally defined as 2,150.42 cubic inches or roughly 1.25 cubic feet (about 35.24 liters)
the U.K. bushel is slightly larger, defined as 2,219.36 cubic inches (about 36.38 liters)

16:16
<bakkot>
:|
16:16
<peetk>
so asking again: Bloomberg has a clear need we've articulated and we are content that the object API solves it. are you a) objecting because you have a different problem that this doesn't solve? or b) you believe we are wrong that it would solve our problem? or c) something else?
16:17
<ljharb>
i believe it would solve bloomberg's problem. i don't think "bloomberg's problem" is broad enough to warrant addition to the language on its own, and i guess i have a different problem this both doesn't solve and makes less likely to be solveable.
16:18
<ljharb>
either way i think it's clear we shouldn't have done BigInt (which was also posited as solving a bloomberg problem)
16:18
<peetk>
ok so you object to stage 1 even.
16:19
<Richard Gibson>
"bushel" is an actual unit, and included in https://github.com/unicode-org/cldr/blob/main/common/supplemental/units.xml . "peck" is a related unit (four per bushel) that is not.
16:19
<keith_miller>
~That doesn't seem like a stage 1 objection~
16:19
<ljharb>
it was not my understanding that the stage 1 problem statement was so constrained so as to preclude the problem i wanted to solve
16:19
<ljharb>
also yeah i think it's still very much a stage 2 objection; it's fine to explore something narrow (which is stage 1)
16:20
<keith_miller>
Oh Matrix doesn't do markdown
16:20
<Michael Ficarra>
~it doesn't?~
16:20
<Michael Ficarra>
hmm
16:21
<ptomato>
you can do it with <del>
16:22
<ljharb>
also what got stage 1 was "BigDecimal" in 2020, and i think the proposal has evolved a lot over the years without reestablishing consensus (because it's not super important to do that within stage 1)
16:22
<ptomato>
use <del>
16:24
<peetk>
ljharb: so if we re-brought a new proposal with the problem statement "we want a native means for doing precise decimal math so we can have a standard interchange format + perf benefits" you would block stage 1?
16:24
<ljharb>
but yeah i'm just not interested in the language having a non-primitive way to do generic math, and i've made that quite clear every time this has come up
16:25
<bakkot>
other people clearly do want that
16:25
<ljharb>
clearly
16:26
<ljharb>
there's a ton of things people clearly want, that aren't in the language because other delegates anti-want it (including decimal primitives)
16:26
<eemeli>
Performance is explicitly not a use case or motivation for Decimal, right?
16:26
<mgaudet>
(Sorry, just a dumb side question: What does this interchange format actually end up looking like in practice? e.g. what does a JSON string with a Decimal value look like in practice)
16:27
<ljharb>
without JSON changing, i assume a string or some kind of custom object
16:27
<eemeli>
A JSON number, I presume?
16:27
<ljharb>
ah, yeah that'd work too, with the raw json stuff
16:27
<dminor>
That was my understanding, but it seems to keep coming up
16:28
<peetk>
when i say "interchange" i mean between JS libraries (although there would presumably be a choice made about how to serialize to JSON)
16:28
<ljharb>
why would such interchange be meaningfully better than "as a numeric string"?
16:28
<eemeli>
Number not being able to represent all JSON numbers is a motivation for Amount.
16:31
<eemeli>
That makes me think that if we end up with a JSON.parse options bag for the reviver, that should maybe include an option for how JSON numbers are represented.
16:32
<bakkot>
someone suggested that IIRC
16:34
<Chengzhong Wu>
strings can represent many things but it doesn't mean it is meaningfully optimal. This is why we need structured, semantic classes and objects
16:35
<Clément Pit-Claudel>

Representative sample of the issue: Express.js/path-to-regex (8.6k stars on github) removing regex support entirely, due to ReDoS:

For version 8, I've removed regex features. I know this is a huge pain for users of the library and apologize in advance.

16:36
<Ashley Claymore>
It is not the primary motivation. But it is a motivation.
16:36
<mgaudet>
(to be clear, I'm not against the problem statement or anything here but one aspect that continues to confuse me is what interchange will actually look like, especially across systems)
16:37
<peetk>
ljharb: i really hate to be a bother but do you mind answering my question at the top of this thread? genuinely i'm really trying to understand your objection
16:37
<ljharb>
i did answer it - https://matrix.to/#/!WgJwmjBNZEXhJnXHXw:matrix.org/$WZd9anGsMD1fcVnfxU9nLxWF47250NKbLa65aPfXHyY?via=matrix.org&via=mozilla.org&via=igalia.com
16:38
<peetk>
so that's a "yes" to "would you block my restatement of the problem for stage 1 if we brought it as a new proposal"?
16:38
<ljharb>
yes. "perf benefits" has been repeatedly debunked by browsers, and re "standard interchange format" i haven't been convinced that it'd be meaningfully better than "numeric strings" as an interchange format
16:38
<ljharb>
and i'm very disinterested in having a number system in the language that doesn't use primitives
16:40
<peetk>
is one delegate not being interested in something sufficient reason to block? (as opposed to, one delegate believing the proposal is harmful e.g.)
16:40
<Ashley Claymore>
In as much as, it's always a nice addition when there is the potential to not download a library and also to implement something natively.
16:41
<ljharb>
by saying "disinterested" and "antiwant" i'm saying that i think it would be harmful to have an object-based number system in the language
16:41
<Ashley Claymore>
One thing I'd really love to do is compare pure JS/Wasm/Native implementations of Decimal128
16:41
<peetk>
thanks, i think it really helps to avoid euphemism in these contexts
16:41
<Ashley Claymore>
just to see what the relative overheads are
16:43
<bakkot>
I can't think of way that subquadratic-superlinear matching comes up with regexes fwiw
16:43
<nicolo-ribaudo>
Good that the size of the regexp is usually constant for a given program and not dynamic depending on outside factors like user input or fs
16:43
<peetk>
can you elaborate on what you think the harm would be?
16:44
<Richard Gibson>
bounded-repetition quantifiers, plausibly
16:46
<nicolo-ribaudo>
Can we always run the two algorithms in parallel on two threads, and race them?
16:59
<bakkot>
oo generator is a fun idea
17:05
<bakkot>
I agree with Mark that timing channels are bad but strongly disagree that this means we must not add any new ones, they just need an adequate justification that I think there we have here
17:08
<Aurèle Barrière>
Thank you for the exciting discussion! Have a good lunch
17:09
<Olivier Flückiger>
Btw. I do like the proposal, but am just not sure about the feasibility (or rather complexity vs. problem tradeoff).
17:09
<Olivier Flückiger>
It might be one of the proposals where early prototyping in actual engines would be really valuable for the discussion
17:14
<Richard Gibson>
I don't think it is justified here, especially since the resource budget could be deterministic
17:14
<Michael Ficarra>
TC53 would like to have a word
17:16
<Steve Hicks>
What about a way to interrupt a regex eval? That would push things like timing considerations outside but still allow libraries to provide a "safe eval with timeout" or whatever
17:17
<Richard Gibson>
yeah, that's space that would be opened up by a generator
17:17
<keith_miller>
Are the problematic regexes for these CVEs known to be risk sites before the CVE arrives?
17:18
<bakkot>
often the CVE takes the form of "I was looking at your regex and noticed it could blow up"
17:18
<bakkot>
they are not usually known to the author to be risk sites
17:18
<bakkot>
but also they are not usually found by active exploitation
17:19
<keith_miller>
So, it sounds like any solution effectively has to be the default to be to solve the CVE problem here?
17:19
<Steve Hicks>
generators can be awkward to use - a cancel token might be more ergonomic
17:19
<bakkot>
I think the existence of this feature would make people think about it more when writing, in a way they currently do not
17:19
<bakkot>
cancel token only works if it's async which is impractical for many uses
17:31
<Caio Lima>
On summary, should we include discussions that happened after the timeslot, or just what got discussed on plenary?
17:35
<eemeli>
I would presume that referring to discussions on this channel in the summary would be fine.
17:38
<Lea Verou>
How would that look like?
17:39
<waldemar>
Rust's regex implementation is not linear in the size of the regex. Things like quantifiers and Unicode character classes cause problems. To prevent denial of service attacks via user-specified regular expressions, the engine includes an analogue of a timeout or resource limit that aborts on some regular expressions.
17:43
<bakkot>
looks like they document this as being linear in the size of the regex "where the size of the regex includes the expansion of all counted repetitions". there are finitely many character classes so those are definitionally at most constant size although the factor can of course be large
17:44
<Lea Verou>
The generator version would also let authors turn it into async, or use scheduler.yield() to yield to the event loop etc. It's not just about allowing vs aborting, it's also about handling. It could also be used for UX mitigation, i.e. "I still want the answer, but this is taking too long so I need to plan accordingly and communicate this to the user"
17:46
<Lea Verou>
It's not just about vulnerabilities either, there are well-intentioned inputs (for either a user-generated regex or the matched string) that can be slow to match
17:47
<Lea Verou>
I agree that generators are awkward to use, but that's something we should solve separately :)
17:59
<waldemar>
Yes, "a{9}{9}{9}{9}{9}{9}{9}" counts as a 4782969-character regular expression in Rust.
18:25
<Michael Ficarra>
it's almost like we should have done private symbols in the first place...
18:26
<peetk>
export for in general would be interesting
18:27
<ptomato>

side note, there's a slightly easier way to export module-scoped access to private fields than what was in the slide (but still I'd prefer private declarations)

let myField;
class MyClass {
  #myField;
  // ...
  static #exportedMyField(instance) {
    return instance.#myField;
  }
  static {
    myField = MyClass.#exportedMyField;
  }
}
18:33
<ptomato>
(well, I guess my way is more verbose, so easier is subjective, but it doesn't require the classes to be nested)
18:33
<Justin Ridgewell>
This is a mistake we made when adding class fields. It would have been much cleaner if we required class { private #x = 1 } to declare in the private field. Then class { #x = 2 } could just refer to the outer.
18:34
<ljharb>
literally anything could work eg { gimme #x: 3 }
18:34
<ljharb>
i would have objected to the spelling "private" but i would have preferred some keyword, yes
18:36
<eemeli>
Can we extend the timebox given that we're ahead of schedule?
18:36
<Michael Ficarra>
I think Kevin might prefer to spend the time on unblocking async iterator helpers
18:37
<James M Snell>
V8 (I don't know about other runtimes) have had the private symbol registry for a while.. e.g. Symbol::ForAPI(...) in the embedder API. Would much prefer an approach like that with the ability to restrict private symbols from being iterated using getOwnPropertySymbols ... So... something like const x = Symbol.private('foo'); ... and private symbols don't enumerate but otherwise act just like other symbols
18:37
<Chris de Almeida>
we still have consensus items to get to, so it would be better to instead do a continuation later if possible
18:37
<James M Snell>
Basically think we can address the use case without any new syntax
18:38
<Chris de Almeida>
if the queue was not so large, sure
18:38
<ljharb>
we can, but the ergonomics would be worse - so why?
18:38
<ljharb>
(we can do both in either order, also)
18:38
<Michael Ficarra>
This being a global registry defeats the visibility restriction, right?
18:39
<ljharb>
although the "no new way to add a private symbol to an object" makes it trickier
18:39
<James M Snell>
No, ForAPI(...) is a global registry that is not exposed to user code
18:39
<James M Snell>
it's distinct from Symbol.for(...)
18:39
<Michael Ficarra>
guess it was the other way around
18:40
<ljharb>
i don't think it'd be better to have it be string-based tho
18:40
<ljharb>
two totally different files could make a private symbol "foo" and shouldn't be able to access each other's data
18:40
<ljharb>
and without syntax, it couldn't be lexical (which is why private fields are syntax)
18:40
<James M Snell>
Symbol.private('...') would not be able to give you from a registry... just keying off the same basic idea as ForAPI in that it's not accessible to users unless specifically handed to them
18:41
<James M Snell>
In any case, I'm +1 on Stage 1 but I think the approach should be fundamentally different than what is being proposed
18:41
<ljharb>
what's wrong with using syntax tho?
18:41
<Chengzhong Wu>
there are two methods to get a private symbol in V8 https://github.com/v8/v8/blob/3105a7cb526e310f2cf996769ba430a99619258a/include/v8-object.h#L53-L68 . Basically the equivalent to Symbol(...) and Symbol.for(...)
18:41
<Michael Ficarra>
it's already stage one 😅
18:41
<ljharb>
it could still become private symbols later
18:42
<James M Snell>
Typical issues with introducing new syntax, etc,etc... can be solved with API rather than syntax
18:42
<ljharb>
"confining it to lexical scope" requires syntax tho. and that's a feature of this proposal imo, that you can't hand it out
18:43
<Michael Ficarra>
"a thing that is exactly like private fields but reified" 😍😍😍😍😍
18:43
<keith_miller>
Olivier Flückiger: "what does private #hello get you couldn't get from const hello = Symbol("hello")" It's sugar around that with defining everything non-enumerable
18:43
<ljharb>
eemeli: yes
18:44
<Olivier Flückiger>
I think it's more. private also does not show up getOwnProperty
18:44
<ljharb>
all symbols are public
18:45
<keith_miller>
That's because there's not an identifier? If you don't have the symbol then you can't getOwnProperty
18:45
<keith_miller>
Maybe there's some thing I'm missing from private fields
18:45
<ljharb>
Object.getOwnPropertySymbols exists
18:45
<ljharb>
so all symbols on an object are always reachable
18:46
<ljharb>
(also Reflect.ownKeys)
18:46
<keith_miller>
Does that give you non-enumerable?
18:46
<keith_miller>
Oh ok
18:46
<ljharb>
yes
18:46
<ljharb>
enumerability of symbols only impacts object spread/Object.assign
18:46
<Steve Hicks>
FWIW, not having "final" (classes and methods) also makes invariants impossible in nearly any case where you're dealing with classes.
18:46
<ljharb>
James M Snell: no, gOPS does not ignore enumerable false
18:46
<Caio Lima>
Can't Proxies leak symbols on access? Or do proxies skips non-enumerable?
18:46
<ljharb>
not as of the introduction of private fields.
18:47
<ljharb>
proxies leak them also, yes.
18:47
<ljharb>
all symbols are fully public in every case full stop
18:47
<keith_miller>
Well with export it it's not very private since a module can't decide who can import it
18:47
<ljharb>
exporting it is nonviable, i have a later topic about that
18:49
<keith_miller>
It also seems like it's conflating two goals for private to export it
18:49
<James M Snell>
> s = Symbol()
Symbol()
> m = {}
{}
> Object.defineProperty(m, s, { value: 1, enumerable: false })
{}
> Object.getOwnPropertySymbols(m)
[ Symbol() ]
> Object.getOwnPropertyDescriptor(m, s)
{ value: 1, writable: false, enumerable: false, configurable: false }
> m[Object.getOwnPropertySymbols(m)[0]]
1
18:49
<ljharb>
line 8 shows that it's there, yes
18:49
<ljharb>
(also Reflect.ownKeys includes non-enumerable symbols also)
18:49
<James M Snell>
That's what I'm referring to
18:50
<ljharb>
that snippet demonstrates that Object.getOwnPropertySymbols includes nonenumerable symbols
18:50
<James M Snell>
Yes, that's what I'm saying
18:50
<ljharb>
ah ok
18:50
<ljharb>
your queue item implied the opposite? maybe i misread
18:51
<James M Snell>
maybe I misworded... the point being, we need private symbols that do NOT show up in enumeration so they can actually be private
18:51
<ljharb>
ok agreed :-)
18:52
<ptomato>
I think private declarations as Kevin is proposing would be better than symbol property descriptors with enumerableNoReallyIMeanItThisTime: true
18:52
<James M Snell>
If we eventually have syntax for making symbols easier, then cool, but we can address this with API ... Symbol.private(...) return a regular Symbol that is truly not enumerated
18:53
<ljharb>
then you'd be able to add it to arbitrary existing objects. and that's an anti-goal
18:53
<Olivier Flückiger>
I think there are arguments for both. a private symbol is a more primitive concept that you could use to build this and also many other things with it
18:53
<James M Snell>
would require an additional slot on Symbol and a tweak to getOwnPropertySymbols/R.ownKeys
18:53
<ptomato>
why would we add that additional primitive concept when we already have the concept of private fields
18:54
<Caio Lima>
How would it work on scope? Every call of Symol.private("foo") return a different Symbol?
18:54
<James M Snell>
I don't see this as a problem. We do this in node.js cases and it works just fine
18:54
<bakkot>
eemeli: private #x; introduces a #x into the nested scope, such that you can refer to it in that scope. So does class { #x }. that's already how it works.
18:54
<Michael Ficarra>
FYI: whenever we end an agenda items with a non-empty queue, the queue is captured in the Log tab (press 3 in TCQ)
18:54
<ljharb>
that's the point. without being syntax, it can't work
18:54
<James M Snell>
Yes, just like Symbol(...)
18:54
<bakkot>
the private #x syntax is new, but the "introduce a #x you can refer to in that scope" is just how private fields work
18:54
<ljharb>
"works just fine" is subjective, and doesn't mean it'd work when everyone has access to the feature
18:54
<James M Snell>
only difference is it doesn't show up in enumerations at all
18:54
<Olivier Flückiger>
huh, #private hello is literally const hello = Symbol.private()
18:55
<Olivier Flückiger>
nothing else
18:55
<ljharb>
it'd still leak with proxies though, including with privateSymbol in arbitraryObject, which imo makes it a bit of a nonstarter
18:55
<Chengzhong Wu>
it can not do obj.foo = #hello
18:55
<Olivier Flückiger>
ok, so it's a untouchable object?
18:55
<ljharb>
it's not an object, it's an unreified PropertyKey - a PrivateName, which is already in the spec
18:55
<Chengzhong Wu>
like, not a first class value, is it?
18:56
<ljharb>
no
18:56
<Olivier Flückiger>
that is the def. of a non-firstclass. it's a unique value but you can only refer to it in some places
18:56
<James M Snell>
I guess I'm less concerned with proxies and less concerned with making it fool proof... would prefer we just pave existing path rather than invent new syntax. Just me tho
18:57
<ljharb>
i get that. but "making it foolproof" is the reason private fields are already so valuable
18:57
<bakkot>
if you read the slide it had export { #foo } for 'importer.js' so you would be deciding who can import it for exactly that reason
18:57
<ljharb>
(and why we're better than every other language because we actually have private state that can't be runtime-introspected, whereas every other language has "_DO_NOT_USE_THIS_OR_BE_FIRED")
18:57
<Olivier Flückiger>
meta comment: I think our timeboxes feel way to small this time.
18:58
<Michael Ficarra>
I think it was only KG's timeboxes that were way too small
18:58
<Caio Lima>
There's also the early error of used but not defined that private fields brings. With Symbols it needs to become a runtime error.
18:58
<bakkot>
I did not expect this one to be controversial
18:58
<Chris de Almeida>
yeah, but we still always want to capture it because chairs often drain the queue before moving on. (cognitive momentum from TCQ Legendary Edition (LE))
18:58
<bakkot>
I should have started with a recap of "what are private fields and why do we have them"
18:58
<bakkot>
because it seems like there is a lot of confusion about those questions
18:59
<Michael Ficarra>
hopefully you're at least using the copy queue button
18:59
<Chris de Almeida>
I mean.. I am
18:59
<Chengzhong Wu>
is there a copy queue button?
18:59
<ljharb>
for one, i think it would make it much more likely we'll never have proper numerical math primitives, and that would be an incalculable harm
19:00
<Michael Ficarra>
chairs have all sorts of features, which includes a copy queue button, yes
19:00
<Lea Verou>

What I was going to say, in case it triggers any useful ideas:

Similar concerns as eemeli . Without this being first-class there are too many open questions about how these behave. E.g. Object.assign(), Object.defineProperty(), exports etc.

But the motivation seems clear, many authors already use symbols that way and the fact that they are exposed via getOwnPropertySymbols() is a surprise to some. But like eemeli , I do wonder if we need a whole new primitive for this however.

First, is lexical scope actually part of the requirements? Or just the ability to assign properties that are not visible to the outside unless explicitly shared, essentially like a more ergonomic version of the weakmap pattern?

Seeing the slides my mental model was that this looks a lot like symbols with
(a) more ergonomic syntax, that
(b) cannot be accessed via the usual ways of accessing symbols

I wonder if it would be expressed in those terms, then it becomes predictable and the semantics fall directly out of that. Exports work, etc.
With a new descriptor, it could be completely decoupled from syntax and even be applied to string properties too. Then #foo is only syntactic sugar to create and register such symbols that automatically have the right descriptor (just like symbols are automatically enumerable: false)

19:00
<bakkot>
the point is, we already have those. that is already what private fields are
19:00
<bakkot>
this proposal does not change that, at all, in any way
19:00
<Olivier Flückiger>

really? how would we catch

private #foo
class NotFoo {}
f = new NotFoo()
f.#foo
19:01
<ljharb>
obv that'd be a runtime error
19:01
<eemeli>
My problem with this is that it forces me to think of #x as a thing, while in the current code I can think of just the # as syntax for "private", and x as the field name.
19:02
<ljharb>
why as a thing? it's just like a variable
19:02
<ljharb>
does let x; make you think of x as a thing?
19:02
<Caio Lima>
You declared #foo there. you can't just have this.#foo without a declaration.
19:03
<Olivier Flückiger>
same with const foo = Symbol()
19:03
<eemeli>
Yeah, x there is something that is a first class thing, and that's completely fine.
19:03
<bakkot>
did #x in foo not already do that?
19:03
<ljharb>
no it's not. x is a second-class thing that refers to a first-class value. you can't pass around the binding such that x = somethingElse automatically exposes somethingElse to the holder of x
19:03
<Justin Ridgewell>
^ Yah, that'll runtime error when you access f.#foo because NotFoo doesn't declare the field.
19:04
<Olivier Flückiger>
exactly, and that is why this is not fully syntactic and not fully first-class either
19:04
<ljharb>
it's not fully syntactic in class fields already
19:05
<ljharb>
static get(x) { return x.#foo; } is a runtime error when it doesn't have the field, already
19:05
<Caio Lima>
Not even with let declarations, given TDZ
19:05
<bakkot>
I should really write up the spec text; I think it may help clarify the extent to which this is not adding any new kinds of thing at all on top of how private fields already work
19:05
<ljharb>
(and const and super)
19:06
<Michael Ficarra>
I don't think anyone reads the proposal spec text. Even this meeting, I feel like I was the only one who actually read the spec text for numerous agenda items.
19:07
<eemeli>
Here too the # parses as syntax in my head. This is of course a rather subjective opinion.
19:07
<bakkot>
it... is syntax?
19:07
<ljharb>
the presence of the # should indicate it's syntax yes. same in all of kevin's slides examples
19:07
<Michael Ficarra>
lol wait what would a # be other than syntax?
19:08
<James M Snell>
fwiw, I 100% agree with things like { [#foo]: 1 } to declare a private in a non-class object... I'm only having a problem with the approach private #foo; class Foo { [#foo] = 1 }; let f= f = new Foo(); console.log(f.#foo) ... I think that should use a Symbol.private(...) type approach instead... treat them differently, not as regular private fields. But again, just one opinion. I can be swayed and shouldn't be considered blocking.
19:09
<bakkot>
I am very strongly opposed to having a different kind of private field on top of ones we already have
19:09
<Justin Ridgewell>
I think if we need to involve Symbols, this proposal will die
19:09
<Justin Ridgewell>
Any reification into values will kill it
19:10
<eemeli>
The place where it smells to me like too much of a new thing is specifically private #x, which reads like the declaration of #x as something novel.
19:12
<Olivier Flückiger>
btw. I am also not blocking here. I think the main thing I trip over is that it's kind of a novel way of doing it, so I would like to see a bit of a discussion why it can't be done some "traditional" way, e.g. friend classes or private symbols, etc.. I understand there are probably technical problems with all of them. but it should be discussed.
19:12
<Justin Ridgewell>
This is the mistake. It should have always been private #x to declare within a scope, even for class fields.
19:13
<Justin Ridgewell>
Then it wouldn't be different that I've declared in my function scope vs my class scope.
19:13
<bakkot>
I did try to push for that at the last minute
19:13
<bakkot>
but it didn't go through, alas
19:14
<Justin Ridgewell>
We could try it again. Redefine private #x; class { #x = 1 } to refer to the outer field. You need class { private #x = 1 } to get a different inner field.
19:14
<Justin Ridgewell>
There is a change in semantics for nested classes that redeclares the same private, but honestly who is doing that?
19:14
<bakkot>
problem is that class { #x = 1; static { class { #x = 2 } } already shadows rather than being a reference to the outer
19:15
<bakkot>
fair, it is possible we could get away with changing it
19:15
<bakkot>
that said it does feel weird that #x = 1 would sometimes be a declaration and sometimes just a reference, so I don't love it
19:15
<ljharb>
i think there should be a way to declare it
19:15
<ljharb>
and even if we can't change classes, i think we should make that new way work in class bodies too (and i still don't like the spelling "private")
19:16
<Lea Verou>
Why?
19:17
<bakkot>
for one thing, that would expand the scope dramatically
19:17
<bakkot>
and I don't think it warrants such a large change
19:17
<Justin Ridgewell>
Because reification breaks hard private requirement. We required only syntax to ensure it works correctly.
19:17
<eemeli>
Would it work to allow for class { protected #x }, so that class { #y } would effectively become sugar for class { private #y }?
19:17
<bakkot>
it doesn't necessarily but it opens up a bunch of annoying questions like "can proxies intercept it"
19:18
<bakkot>
and some possible answers to those questions would break privacy
19:18
<eemeli>
Where "protected" here would mean that the field is available in the lexical context or whatever.
19:18
<Justin Ridgewell>
protected here wouldn't work like every other language, though. You can't late declare a class to refer to the #x, it would have to be lexical.
19:18
<Lea Verou>
How so?
19:18
<eemeli>
Some other word then.
19:18
<ljharb>
no, "protected" is wholly inappropriate for javascript
19:19
<Justin Ridgewell>
This is what I dealt with when I tried for Private Symbols. It's a very difficult question to address.
19:19
<ljharb>
"protected" (and "private") implies "access levels" which isn't how JS classes work
19:19
<bakkot>
I'm not totally sure what the suggestion is. class { #y } is pretty much already sugar for class { private #y } in the proposal as it stands
19:20
<Lea Verou>
Ah, right. Darn.
19:20
<Olivier Flückiger>
I guess if we wanted symmetry we could also use #foo as the declaration in any scope
19:20
<eemeli>
Suggestion is for the visibility declaration to be a part of the field declaration, not wholly separate from it.
19:21
<Lea Verou>
I suppose making these invisible to proxy traps would be a no-go?
19:22
<Justin Ridgewell>
It breaks membrane transparency when the value is intentionally passed between graphs.
19:22
<ljharb>
(the only intended use case for proxies, iow)
19:22
<bakkot>
sorry, I still don't understand; can you spell it out a little more?
19:23
<Justin Ridgewell>
I had like a 100 slide presentation trying to cover all the cases for how to do it. It becomes incredibly complex.
19:23
<bakkot>
also we settled the question already
19:23
<Michael Ficarra>
@sffc me an JHD
19:24
<Lea Verou>
In the sprit of "code only sees these if it has a reference to them" what if you could supply a list of private symbols you have references to when creating the proxy, and any others are invisible to it and just automatically reflect?
19:24
<Justin Ridgewell>
https://docs.google.com/presentation/d/1HWM_pREmpz7QN9VrNeFt5T-x6CfKBL06pXhiQcxgfgU/edit?slide=id.gc6f73a04f_0_0#slide=id.gc6f73a04f_0_0 is the slidedeck after I pared it back.
19:24
<Michael Ficarra>
@ljharb ooh good reminder, I almost forgot
19:30
<eemeli>

I think it'd be weird to have this:

private #x
class A { static #x }
class B { static #x }

Where the visibility of A.#x and B.#x are both affected by the private declaration. It would be much clearer if the visibility of A.#x and B.#x were defined separately, with syntax immediately adjacent to their definitions as class fields.

19:32
<bakkot>
ah. I agree that we should not have the visibility of A.#x or B.#x affected by the private declaration and am not proposing it that it would
19:32
<eemeli>

So maybe this?

class A { static outer #x }
class B { static outer #x }
19:33
<bakkot>
yeah, you'd need something like that to refer to the outer field
19:34
<bakkot>
I had it as class A { static [#x] } but this seems to have been confusing so probably some sort of keyword
19:34
<bakkot>
outer or with or something
19:34
<rbuckton>
One of these days I should propose Symbol.geti/seti for property get/set inversion. Originally considered for reified Slice/Index-from-end, it could also be used to allow WeakMap as a key using element access/computed property names.
19:38
<Steve Hicks>

If this is an error

private #x;
class A { #x; [#x]; }

then it's not a big stretch to my mind for

private #x;
class A { #x; }

to also be an error. In which case, why do we need another keyword, just use #x as-is to refer to the outer scope. (admittedly, this is a bit of a straw man, since I don't think the first block should be an error, and I'd rather get more clarity on what exactly the scoping and shadowing rules are)

19:39
<Justin Ridgewell>
mgaudet: You're causing an echo
19:39
<Steve Hicks>

In particular

private #x;
{ private #x; }

Is this allowed? It seems equivalent to

class Outer {
  #x;
  static { class Inner { #x; } }
}

which is definitely allowed currently.

19:41
<bakkot>
the error in the first case happens at runtime, when instantiating the class because it will try to install the inner field twice
19:41
<bakkot>
the private #x doesn't have anything to do with it in this case
19:41
<bakkot>
class A { #x; [#x] } is an error for the same reason
19:42
<Steve Hicks>
I guess what I'm saying is - why is that "the same" field? One is #x and the other is [#x], aren't these completely different things?
19:42
<bakkot>
the same reason that a.b and a['b'] are the same field?
19:43
<Justin Ridgewell>
You're redeclared an inner #x. The same way let x; { let x; obj[x] } refers to the inner x.
19:44
<peetk>
but there's no string "#x" in this case. one is a class-private-field-thing and one is a module-private-field-thing. they (plausibly) are different kinds of things
19:44
<Steve Hicks>
I think we have a different mental model for private #x. What about private #x; { private #x; } are these the same thing?
19:44
<bakkot>
no
19:44
<Justin Ridgewell>
No
19:44
<bakkot>
same reason that let x; { let x; } are not the same thing
19:44
<Steve Hicks>
So why is it the same when the inner one is a class?
19:45
<Justin Ridgewell>
The same way let x; { let x } aren't the same var.
19:45
<bakkot>
or class A { #x; static { class B { #x } } } for that matter
19:45
<Steve Hicks>
But you're saying that it is the same in private #x; class A { #x }?
19:45
<bakkot>
no
19:45
<bakkot>
those are also different fields
19:45
<bakkot>
in literally exactly the same way
19:46
<Steve Hicks>
So why is private #x; class A { #x; [#x]; } an error?
19:46
<Justin Ridgewell>
Because the same private name can't be installed on the same object twice.
19:46
<bakkot>
for the same reason that class A { #x; [#x] } is an error
19:46
<bakkot>
the private #x has nothing to do with this
19:46
<peetk>
but they're not the same lmao
19:47
<peetk>
^
19:47
<bakkot>
it's just that class A { #x; [#x] } means "install #x on these instances twice"
19:47
<bakkot>
and that's an error
19:47
<Steve Hicks>
okay so we're interpreting the not-yet-defined [#x] syntax differently
19:47
<bakkot>
the private #x declaration does not come into play in any way
19:47
<ljharb>
(wouldn't be if one was x and one was y tho)
19:47
<Steve Hicks>
I think that's the disconnect
19:48
<keith_miller>
How much value do we get from shadowing? is it something we'd be willing to kill for private #x
19:48
<bakkot>
I get no value from shadowing and would be happy to kill it
19:48
<bakkot>
but we do already have it for classes
19:48
<bakkot>
so I don't see that much value in killing it either
19:49
<keith_miller>
As in private #x class { #x = 1 } refers to the outer
19:49
<keith_miller>
Yeah, idk maybe that's confusing
19:49
<bakkot>
oh, I don't love making #x = 1 sometimes a declaration and sometimes a reference
19:49
<bakkot>
I'd be OK with making that line an error though
19:49
<Steve Hicks>
I guess I don't see class { #x } as defining a "thing" #x in the same way that private #x does precisely because the latter allows [#x] but the former doesn't.
19:49
<Justin Ridgewell>

The double initialization error already exists:

class Base {
    constructor(o) {
        return o;
    }
}
class Sub extends Base {
    #x;
    constructor(o, v) {
        super(o);
        this.#x = v;
    }
}
const o = {};
new Sub(o, 1);
new Sub(o, 2);

VM433:7 Uncaught TypeError: Cannot initialize #x twice on the same object
    at <instance_members_initializer> (<anonymous>:7:5)
    at new Sub (<anonymous>:9:9)
    at <anonymous>:15:1
<instance_members_initializer> @ VM433:7
Sub @ VM433:9
(anonymous) @ VM433:15
19:50
<Justin Ridgewell>
This is using the super override trick.
19:51
<bakkot>
sorry, to be clear, they both would allow [#x]
19:51
<bakkot>
I didn't cover this and I guess I should have
19:51
<Justin Ridgewell>
You never need it in the former case, though? You only need it for classes so that you install the field at declaration.
19:51
<bakkot>
but the point is that private #x introduces a name in exactly the same sense that class { #x } introduces a name
19:52
<bakkot>
and then we would have a syntax { [#x]: 1 } for referring to these existing kinds of names in object literals
19:52
<Justin Ridgewell>
You mean to allow private [#x] at block scope?
19:52
<peetk>
ahhhhh this is what i was missing
19:52
<Justin Ridgewell>
Why would you need to re-refer to the outer?
19:52
<ljharb>
a class could create an object with the private field?
19:53
<bakkot>

specifically I am proposing

class A {
  #x = 1;
  static factory() {
    return { [#x]: 1 }; // makes objects which are not instances of A but have `#x` anyway
 }
}
19:53
<Justin Ridgewell>
Oh, in object scope. Yes, that's allowed.
19:53
<ljharb>
#x = { [#x]: true }; would also work
19:54
<bakkot>
yes although, probably don't
19:54
<ljharb>
oh, definitely don't. it just follows.
19:55
<peetk>
thanks for clarifying.
19:55
<Aki>
I didn't catch a screenshot of the temp check
19:55
<ljharb>
log?
19:55
<Justin Ridgewell>
It's recorded in TCQ log
19:55
<Aki>
ah yes, the log
19:56
<ljharb>
it's better than bad, it's good!
20:00
<Caio Lima>
Thanks all for the participation on topics I presented
20:01
<Aki>
Well that was rather unceremonious so i'll just say REMEMBER TO FILL OUT YOUR SUMMARIES AND CONCLUSIONS seriously, not-doing it just makes more work for me and i really deserve a little time off this summer, right?
20:02
<ljharb>
don't forget to transfer your proposal repos into tc39-transfer! waldemar sffc et al
20:05
<Steve Hicks>
So the cognitive dissonance I'm facing is that we'd like to be able to write {#x:1} and we can't because class {#x=1} is both a declaration and a use, so we need some other syntax to only use the private name and not declare a new one. So we compromise on {[#x]:1} instead, but the analogue between [#x] and #x doesn't work in my brain because it's different for strings - you need the extra quotes: ['x'] vs x. I think that's why I find it surprising that they're the same.
20:06
<bakkot>
yes, very reasonable
20:06
<Steve Hicks>
I think I had the same issue in March with Lea Verou 's protocols proposal.
20:07
<bakkot>

next time I come back it will look like

private #x
class A {
  with #x = 1
}

or something, instead of trying to re-use computed member access syntax

20:07
<bakkot>
with and outer I think are the only reasonable keywords there that I have thought of
20:07
<Steve Hicks>
I really dislove the use of with here... but I admit I'm not thinking of anything better either.
20:08
<bakkot>

and of course similarly for objects

private #x;
function factory() {
  return { with #x: 1 };
}
20:08
<Steve Hicks>
but at least the shadowing becomes a lot clearer
20:08
<bakkot>
(not technically necessary for objects, but weird to omit IMO)
20:09
<Steve Hicks>
right, if we had a time machine we'd probably have been better off adding the syntax for the class declarations so that one syntax didn't do double duty
20:10
<bakkot>
I had that time machine and therefore did try, but was unable to convince the committee
20:18
<peetk>
you could take nonlocal from python which uses it in a way that i think is analogous
20:18
<bakkot>
oh god
20:18
<bakkot>
you're not wrong
21:31
<Justin Ridgewell>
I don't see why this isn't the same as obj = { ['x']: 1 } and obj.x being valid. You don't need to access with obj['x'].
21:42
<James M Snell>
With regards to the Error code property proposal DOMException discussion... I put together a rebuttal: https://github.com/tc39/proposal-error-code-property/issues/2#issuecomment-5051899121
21:42
<Steve Hicks>
That's a matter of perspective. Under Closure Compiler rules, you absolutely do need to access it with obj['x'] - so I think my brain is much more tuned to seeing obj[ and obj. as two completely separate namespaces. I get that in the engine they're the same thing, but I think my point stands that .#x and [#x] don't actually follow the same analogue as .x and ['x'] because even in this analogue you still need both the brackets and the quotes for them to be equivalent, and here we're just adding brackets, there's no analogue for the quotes.
21:46
<Justin Ridgewell>
But why would you need the quotes to refer to the key? Like, why would I need it for const x = 'x'; obj = { [x]: 1 }; obj.x? Or would this really be any better private #x = '#x'; obj = { [#x]: 1 }; obj.#x.
21:47
<Justin Ridgewell>
I see these as being exactly analogous to referring to a local key and accessing that with direct syntax.
22:06
<hax (HE Shi-Jun)>
Never seen this slide before! Why this failed ? 😭
22:23
<bakkot>
fwiw I think enough people found the [#x] syntax immediately confusing that I'm not going to pursue it any more anyway
22:23
<bakkot>
I do not love with #x but it hasn't lead people to this confusion in the same way
22:23
<bakkot>
even if that confusion would ultimately dissolve on sufficient reflection, which it might well, it's also important to be not initially misleading
23:00
<hax (HE Shi-Jun)>
So class { #x = 1 } could be seen as shorthand of class { private #x; with #x = 1 }?
23:11
<bakkot>
yup, exactly
23:11
<bakkot>
though in practice we probably won't have private #x; in class body scopes
23:13
<bakkot>
slightly longer writeup, mostly notes for myself for next time I present https://gist.github.com/bakkot/2272dbe261fb1c69844c723347ddb1a1