01:08 | <Justin Ridgewell> |
|
01:09 | <Justin Ridgewell> | I very much prefer we use _ , I think refactoring hazards are pretty simple to fix since it's a lexically scoped name. |
04:03 | <rbuckton> | java is using _ for void bindings. I'd prefer we use it too, but unless ljharb relaxes his position, it's not an option. |
04:04 | <rbuckton> | I very much prefer we use |
04:06 | <rkirsling> | while I'm typically opposed to chaos, this would be the chaotic move that I'd support |
06:32 | <ljharb> | i'm pretty sure i'm not the only one who objects to violating TCP |
06:34 | <ljharb> | https://fanf.livejournal.com/118421.html and https://esdiscuss.org/topic/regarding-tennent-s-language-design-based-on-semantic-principles are good reading on the topic |
06:37 | <bakkot> | I think you might be the only one who objects in this case |
06:40 | <ljharb> | sometimes one person objecting means other objectors don't speak up. not violating TCP came up a ton around ES6 and i'd be surprised if in fact nobody else cares about preserving that property. |
06:41 | <bakkot> | I do not immediately see how using _ for void bindings would violate TCP |
06:42 | <ljharb> | tbf for TCP i was thinking about pipeline. it's possible that doesn't apply for void bindings, i'll have to take another look |
06:43 | <ljharb> | so would const { _, ...kept } = whatever make _ be a discarded binding but const _ = whatever._ wouldn't? |
06:44 | <ljharb> | for example, https://www.npmjs.com/package/minimist (57M weekly downloads) produces an object with a _ property, would you still be able to destructure it as you can now? |
06:47 | <bakkot> | so there are a variety of ways this could work. the simplest backwards-compatible way would be, no changes to any current programs, but it becomes legal to re-declare _ in the same scope; if you do, any references to _ within that scope are an error (possibly at parse time). |
06:48 | <bakkot> | this would not distinguish between const { _, ...kept } = whatever and const _ = whatever._ in any way |
06:49 | <ljharb> | ok so like, const { _ } = whatever; console.log(_) would continue to work, and if i added const { x: _ } = foo after it, it would discard the new binding but also poison use of _ after that? |
06:49 | <ljharb> | so if i wanted to discard a binding i'd have to rename my existing binding? |
06:50 | <bakkot> | I would want it to poison all uses anywhere in the scope. so you could write
and
but not
|
06:50 | <bakkot> | and yes, it would mean that if you want to use discard bindings, you would need to not name a thing _ in that scope |
06:50 | <ljharb> | ok so in that last example, adding line 3 would create an error where it previously worked |
06:50 | <bakkot> | correct |
06:50 | <ljharb> | and why is that better than const { x: void } = foo which doesn't have that downside? |
06:51 | <bakkot> | because void is so many more characters, and means a different thing |
06:51 | <bakkot> | whereas _ is the convention across many programming languages |
06:51 | <ljharb> | ok, but most JS devs won't have ever used another language. |
06:51 | <ljharb> | and 4 chars isn't that many more than 1, but sure, technically 4 > 1 |
06:52 | <bakkot> | I dispute that claim and also I think we should still care about consistency with other languages even if it were true |
06:52 | <ljharb> | and void already means "ignore this thing". it's just that existing uses in JS also mean "… and produce undefined ". in TS it literally means "you can't use this thing". |
06:54 | <ljharb> | iow i totally accept the argument of "_ is what most other langs use", that's empirical; as is "4 chars is more than 1". but i find the latter a weightless argument, and the former an argument only when looking at similar alternatives. |
06:55 | <rkirsling> | I don't think the average JS app developer is using void 0 so I would say any JS devs who have an association with void as a keyword would be thinking of it as a return type |
06:55 | <ljharb> | _ is a perfectly reasonable thing to use in a language where it's not a valid identifier, to be clear - precisely because it's short and connotes "nothing" |
06:55 | <ljharb> | I don't think the average JS app developer is using |
07:10 | <rbuckton> | _ was already a legal identifier. |
07:11 | <ljharb> | my experience with scala tells me that conflating what "_" means, specifically, is a bad idea - i think there's 18 meanings of it depending on context? (update: https://stackoverflow.com/a/8001065/632724 has 16) |
07:12 | <rbuckton> | If we held the "don't repurpose identifiers" position back in es2015, we would not have yield or await . Those are valid because you had to opt in to the new syntax. Every case where we've proposed coopting _ has also been new syntax. |
07:13 | <ljharb> | i think for those it was because the entire containing function had to use new syntax. this is just adding a line. |
07:18 | <rbuckton> | For pipeline, it's fairly obvious that the boundary is the |> expression. For discards, it's a little less clear, but we could even go so far as to issue early errors if you reference a _ declared in a scope with more than one declaration of _ . It would be immediately obvious and indicate a need to refactor instead of silently picking a _ declared in an outer scope. |
08:35 | <nicolo-ribaudo> |
|
10:41 | <rbuckton> | Also note that already today adding the third line here makes the code an error, it's not something new var |
11:46 | <nicolo-ribaudo> | I would assume we don't change the behaviour of var |
11:46 | <nicolo-ribaudo> | var already allows assigning to _ as much as I want |
11:47 | <nicolo-ribaudo> | The problems are let/const and strict function parameters |
12:43 | <rbuckton> | Using _ as a discard in var would not be a discard. It would not have the safety guarantees we're discussing. If you have existing code that uses a var _ and a reference to _ , adding new code with _ as a discard would not result in an early error and would cause that code to reference the wrong value. |
14:40 | <shu> | https://arxiv.org/pdf/2403.11919.pdf |
16:11 | <TabAtkins> | From the Java proposal:
lol that doesn't apply to JS |
16:15 | <Jesse> | _ should be safe |
16:16 | <TabAtkins> | How is _ safe in a page that uses underscore.js? |
16:27 | <Chris de Almeida> | and lodash, for anyone unfamiliar with underscore |
16:27 | <TabAtkins> | Ah, and it looks like Java has done the Work to make it safe - Java 8 issued warnings for using _ as an identifier, and 9 made it an error. So the pain is just people who had previously compiled only on Java 7 trying to upgrade to a modern Java. |
16:29 | <nicolo-ribaudo> | Well Java introduced it through a breaking change — we wouldn't change the meaning of existing valid code, but just relax some errors |
16:42 | <rbuckton> | _ should be safe __ is worse than void . _ makes sense as it's a single character and has precedence in many languages. feels way more arbitrary |
16:43 | <littledan> | I'm pretty sure __ was joking... |
16:50 | <Jesse> | it's a single non-ASCII character |
16:51 | <ptomato> | … |
16:54 | <Justin Ridgewell> | How is _ safe in a page that uses underscore.js? |
16:55 | <Justin Ridgewell> | We don’t need to make _ invalid, we just need to allow reassignments in new code. |
16:55 | <littledan> | sure, I guess the principle is that new features should work nicely, when mixed together with existing code |
16:55 | <littledan> | (when possible) |
16:56 | <nicolo-ribaudo> | We can allow reassigning to all identifiers, moving existing redeclaration errors to errors on reference access |
16:56 | <nicolo-ribaudo> | And underscore.js users can use $ |
16:56 | <nicolo-ribaudo> | And jQuery+underscore can use $_ |
16:57 | <Justin Ridgewell> | @bakkot’s idea would work for that. And in cases where it causes a conflict, renaming a local, lexically-scoped variable is trivial and automatable. |
16:59 | <Anthony Bullard> | Why not something novel like *? I’m sure there’s a good reason |
16:59 | <littledan> | so there are a variety of ways this could work. the simplest backwards-compatible way would be, no changes to any current programs, but it becomes legal to re-declare |
16:59 | <nicolo-ribaudo> | Yes |
16:59 | <Anthony Bullard> | I mean * |
16:59 | <nicolo-ribaudo> | let *= a is already valid |
17:00 | <Anthony Bullard> | nicolo-ribaudo: ? |
17:00 | <nicolo-ribaudo> | You are multiplying the let variable by a |
17:00 | <Anthony Bullard> | Let is a keyword |
17:02 | <Anthony Bullard> | Am I missing something obvious? |
17:02 | <iain> | Let is only reserved in strict mode |
17:02 | <littledan> | sloppy mode |
17:02 | <littledan> | let didn't used to be a keyword; you can still use it as a variable sometimes |
17:03 | <littledan> | you can switch into this mode with eval() |
17:06 | <Anthony Bullard> | I really don’t love void bindings in bare assignments, ie not destructuring |
17:06 | <littledan> | wouldn't it be weird to distinguish them, though? |
17:07 | <Anthony Bullard> | Yes but a throwaway binding is meant to throw something away that you have to reference but don’t want to bind in the scope. |
17:07 | <Anthony Bullard> | But I’ll concede it’s messy |
17:08 | <TabAtkins> | _ should be safe |
17:10 | <Anthony Bullard> | Man you really gotta make it obvious when you're doing unicode shenanigans (U+ff3f FULLWIDTH LOW LINE, not ASCII underscore) ^_^ |
17:12 | <TabAtkins> | This is why CSS took a (technically non-binding) resolution on itself to limit its built-in syntax to the ASCII range, like a decade ago. |
17:12 | <TabAtkins> | (Non-binding because we can always reverse our own resolutions.) |
17:17 | <Anthony Bullard> | Honest question: would it be better to change the binding rules for an existing (and widely used) identifier, or using a new piece of syntax for this that only works in strict mode? Is there any precedent for the latter? |
17:27 | <ljharb> | My preference is still to use a new piece of syntax. void satisfies that and works in sloppy mode too. there's certainly precedent for strict-only syntax as well. |
17:29 | <bakkot> | Honest question: would it be better to change the binding rules for an existing (and widely used) identifier, or using a new piece of syntax for this that only works in strict mode? Is there any precedent for the latter? |
17:32 | <Anthony Bullard> | that's a matter of opinion, but given that the former would allow us to be consistent what ~every other language does, it seems like the better option by far |
17:54 | <bakkot> | I suppose _ has some awkward interactions when used in assignment position |
17:56 | <Anthony Bullard> | I don’t love the keyword void, but that’s just aesthetic mostly. I’d love to have some piece of syntax that could be used with no ambiguity in strict mode |
18:00 | <nicolo-ribaudo> | Even in strict mode, * (or other operators) don't work in using declarations |
18:00 | <nicolo-ribaudo> | using *= x |
18:05 | <Anthony Bullard> | Even in strict mode, |
18:09 | <rbuckton> | Damn you’re right. Is there any symbol that would work? @ and # have alternative semantic meanings (decorators and private names) that make them a bad fit, plus @ and # have already been considered for infix operations in the past, and I'd rather not close off that syntax space just for discards. |
18:10 | <Anthony Bullard> | What about ? |
18:11 | <rbuckton> | ? is proposed as a special token for partial application, and had already had feedback bout "too much ? " considering conditionals, optional chaining, null coalesce, and null coalesce assignment. |
18:12 | <Anthony Bullard> | It’s use in partial application is what made my think of it |
18:12 | <rbuckton> | I've also proposed using ~ as part of partial application since one of the other concerns was the need for a token to opt-in to the behavior, so foo~(1, ?, 2) as a partial application of foo that supplies the first and third argument with a placeholder for the 2nd argument. |
18:14 | <Anthony Bullard> | I've also proposed using |
18:14 | <rbuckton> | I don't think any other ASCII symbols that are generally available on most keyboards match the semantic behavior of a discard. |
18:15 | <rbuckton> | This example showed me maybe partial application is a bad idea in JS, as much as I love it in FP languages |
18:15 | <Anthony Bullard> | We are just running out of concise syntax |
18:15 | <rbuckton> | We've already run out of concise syntax. One of the blockers for pipeline has been picking a topic token. |
18:16 | <Anthony Bullard> | We've already run out of concise syntax. One of the blockers for pipeline has been picking a topic token. |
18:16 | <rbuckton> | I'll admit, I'm still partial to F#-style pipes and papp without the ~ , regardless the direction the pipeline proposal took. |
18:18 | <Anthony Bullard> | ? Being a operator that in an expression context meaning “something referred to but not bound” is somewhat elegant on its own |
18:18 | <rbuckton> | Two of biggest FP-ish libraries use opposite positions (lodash and underscore are data-first, Ramda is data-last). The benefit of papp was that you didn't have to "pick a winner" |
18:18 | <Anthony Bullard> | Not operator, but syntactic construct |
18:19 | <rbuckton> | You can't have ? stand on its own in a given expression without some kind of boundary. For papp, I wanted that boundary to be Arguments |
18:20 | <rbuckton> | So, add(1, ?) would have been fine, but add(1, { option: ? }) would not, because it goes from being an Argument placeholder to being an arbitrary expression. |
18:20 | <Anthony Bullard> | The boundaries could be:
|
18:21 | <rbuckton> | I'm not sure I understand what you mean by the first three. |
18:21 | <Anthony Bullard> | Lhs |
18:21 | <rbuckton> | I mean, what would ? = x even mean? |
18:22 | <Anthony Bullard> | I’m not sure, but above it was communicated that there is a desire to have a discard token be the same in destructuring and bare assignment |
18:22 | <Anthony Bullard> | The use case for the latter is unclear to me |
18:24 | <rbuckton> | For discards, void = x is currently disallowed because it is meaningless. |
18:24 | <Anthony Bullard> | let ? = X is weird for sure |
18:24 | <Anthony Bullard> | So drop that |
18:25 | <Anthony Bullard> | And leave it to array and object destructuring and Papp arguments |
18:25 | <Anthony Bullard> | Maybe I should be having this discussion in public on the proposal? |
18:26 | <rbuckton> | Also true. In the proposal it is only present for two reasons:
Both are very weak reasons. |
18:26 | <rbuckton> | This was discussed in the last plenary. |
18:26 | <rbuckton> | I just haven't had time to update the proposal explainer with those outcomes yet. |
18:27 | <rbuckton> | It's also already mentioned here: https://github.com/tc39/proposal-discard-binding/issues/1 |
18:27 | <Anthony Bullard> | This was discussed in the last plenary. |
18:33 | <Jack Works> | some language repurpose identifiers as special things. that's good, but I don't think we have that tradition in js. by "repurpose identifiers as special things" I mean new things are using slightly different visual feelings in the common use cases. e.g. "var yield = f()" and "yield x + y" don't feel the same. (I know there are cases that are valid in both interpretations like yield[expr] so it's up to syntax parameters) repurposing _ is not what we do to the yield and await. The new use case of _ looks very similar to the old one (old "_.add()" new "_ = x") so I don't think it's a good addition |
18:36 | <rbuckton> | On the papp/pipeline side, I still think F# pipes and papp would have been far more likely to get to stage 2 as they were general purpose capabilities that dovetailed. F#'s style biggest drawbacks were how to handle
I just don't see
|
18:36 | <Anthony Bullard> | Definitely, but if we can find a symbol that’s not used in identifiers that in a context would be unambiguous and could be used for discard AND Papp topic token that would be a win in my eyes |
18:37 | <Anthony Bullard> |
|
18:38 | <Anthony Bullard> | Though they also are creating their own operators too |
18:40 | <rbuckton> | I don't think using the same token for discard and papp is a good idea. In the pattern matching space, I've been considering how partial application could allow you to supply arguments to a custom matcher:
as a way to synthesize an extractor in-place as opposed to
|
18:41 | <rbuckton> | You have not encountered enough point free F# people 😂 |
18:42 | <rbuckton> |
|
18:42 | <Anthony Bullard> | There was a tremendous amount of very vocal frustration in the RxJS community that pipeline did not use F# style. |
18:43 | <rbuckton> | It's still very popular and heavily used in React projects. Rx's Observable has already been a proposal to TC39 (which stalled), and is now a proposal in WHATWG |
18:44 | <bakkot> | (though the WHATWG one is substantially pared down, which is good) |
18:44 | <Anthony Bullard> |
These are done different, so I don't know if this is a good comparison. I really don't see the difference :
|
18:45 | <rbuckton> | That would have been const identifierMatcher = NodeMatcher(SyntaxKind.Identifier, ?) |
18:45 | <Anthony Bullard> | It's still very popular and heavily used in React projects. Rx's Observable has already been a proposal to TC39 (which stalled), and is now a proposal in WHATWG |
18:45 | <rbuckton> | the issue is that if ? is both a placeholder and a discard, parsing when F(?) isn't obvious to either the spec or the reader of code. |
18:45 | <Anthony Bullard> | That would have been |
18:47 | <rbuckton> | when F(?)(?): would be a partial application to a destructured void binding. But you don't need to destructure a match, so when F(?) is now ambiguous as to whether I'm matching the subject against a partial application of F or against F 's custom matcher. |
18:47 | <Anthony Bullard> | Yeah, with the expanded destructuring proposals, it does have problems |
18:47 | <rbuckton> | So discards and papp cannot share a sigil |
18:48 | <Anthony Bullard> | And papp cannot use _ right, since that is a valid identifier |
18:48 | <rbuckton> | Yes. Papp definitely can't use _ . |
18:48 | <Anthony Bullard> | In case I want to supply lodash to my partially applied function |
18:48 | <rbuckton> | a(_) is legal JS, we can't change it's meaning. |
18:50 | <rbuckton> | whereas const [_, _, x] = ar is not legal thus we are not changing its meaning. |
18:53 | <rbuckton> | The only place using _ as a discard would change the meaning of legal JS is if it is used with var . |
18:53 | <Anthony Bullard> | So just to recap this long convo, is ? as a replacement for void in your proposal (and that alone) a live option? |
18:53 | <rbuckton> | No, I don't believe it is. |
18:54 | <Anthony Bullard> | No, I don't believe it is. |
18:54 | <rbuckton> | I also don't believe @ , # , or ~ are viable options a well. |
18:55 | <rbuckton> | It would seriously conflict with an existing proposal at stage 1 and leave that proposal with no viable way forward, whereas we could choose to continue with void or find a way to make _ work and not conflict. |
18:55 | <Anthony Bullard> | Sorry, what proposal? |
18:55 | <rbuckton> | Partial application. |
18:56 | <rbuckton> | I am still planning to come back to partial application, it has just been far down on my list of priorities. |
18:58 | <rbuckton> | I also don't think ? has a semantic meaning of "discarding something". It generally has a semantic meaning of "asking something": "what do I do if x is truthy?" (conditional), "is this value null or undefined?" (optional chaining, null coalesce), or "what else do you need to tell me?" (partial application) |
18:58 | <bakkot> | Give me a standard library before Observables. I'm actually going to start saying that in reference to every new API at this point |
18:59 | <rbuckton> | Discarding things in JS is either through void (as a syntactic feature of the language), or through _ (on its own or as a prefix, by convention). |
19:02 | <rbuckton> | Observables and cancellation really should have stayed in TC39. They're core language capabilities that are not specific to the web platform, and it's unfortunate the ecosystem had to adopt whole chunks of the DOM into non-DOM runtimes just to support code sharing and interop. |
19:03 | <rbuckton> | Observables/signals/events/etc. JS could really use a standardized event subscription mechanism, and the closest we got was Promise . |
19:04 | <Anthony Bullard> | Discarding things in JS is either through |
19:04 | <rbuckton> | And we were quick enough to adopt it before WHATWG moved ahead with Future , otherwise we would not have anywhere near the flexibility of async /await in the language. |