| 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 |
| 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 I think what's increasingly confusing is only |
| 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> |
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> |
|
| 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> | <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> | <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:
|
| 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)
|
| 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> |
|
| 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. 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 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. |
| 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
|
| 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:
Where the visibility of |
| 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?
|
| 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
then it's not a big stretch to my mind for
to also be an error. In which case, why do we need another keyword, just use |
| 19:39 | <Justin Ridgewell> | mgaudet: You're causing an echo |
| 19:39 | <Steve Hicks> | In particular
Is this allowed? It seems equivalent to
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:
|
| 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
|
| 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
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
|
| 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 |