17:15 | <ljharb> | mathiasbynens: is there a reason that we wouldn't want something like https://github.com/orling/grapheme-splitter in the language? |
17:53 | <mathiasbynens> | ljharb: that’s Intl.Segmenter |
17:59 | <ljharb> | mathiasbynens: right, but i meant directly on strings |
18:46 | <jridgewell> | What are you asking for? |
18:46 | <gibson042> | what would you expect from implementations that don't include ECMA-402? |
18:46 | <jridgewell> | What would "directly on strings" mean? |
18:47 | <devsnek> | like String.prototype.graphemes or something? |
18:47 | <ljharb> | sure |
18:48 | <jridgewell> | I think that'd run a huge risk of breaking changes? |
18:49 | <jridgewell> | Every time we update Unicode, you're `"foo".graphemes` could return something different. |
18:49 | <jridgewell> | your** |
18:50 | <ljharb> | breaking changes as a result of updating unicode already happen and are already Fine™ |
18:50 | <ljharb> | the same could occur with code points, and *has* occurred with whitespace |
18:51 | <jridgewell> | As a syntax change, early errors are easy to fix. |
18:51 | <jridgewell> | This is a change in runtime behavior. |
18:51 | <jridgewell> | Much more difficult to track donw. |
18:53 | <ljharb> | right, those already have happened and will continue to happen when unicode updates |
18:53 | <jridgewell> | How? |
18:53 | <ljharb> | `.trim()` changed, for example, wrt the mongolian vowel separator in ES2017 iirc? |
18:53 | <ljharb> | that character got classified as whitespace, and then unclassified as it (or maybe the reverse, i don't recall) |
18:53 | <ljharb> | ie the meaning of `\s` in regexes changed |
18:54 | <ljharb> | so i don't think that "unicode changes" is an argument against adding any string-related features |
18:54 | <devsnek> | what is the benefit of having the api on string.prototype |
18:54 | <devsnek> | we already have the segmenter api |
18:54 | <ljharb> | devsnek: Intl isn't required, it's optional |
18:54 | <ljharb> | devsnek: so it's not guaranteed to be available |
18:54 | <devsnek> | the prototype method would have to be just as optional |
18:55 | <devsnek> | like toLocaleString |
18:55 | <rkirsling> | ^ |
18:55 | <ljharb> | why would it have to be? |
18:55 | <ljharb> | code points depend on unicode, and Symbol.iterator is required |
18:55 | <ljharb> | (toLocaleString is also required, it's just that its behavior is impl-dependent) |
18:55 | <ljharb> | (and 402 overrides it when present) |
18:56 | <rkirsling> | yeah but I don't believe XS uses ICU, say |
18:57 | <mathiasbynens> | segmentation is locale-dependent, at least for words and sentences, so Intl.* makes sense |
18:58 | <mathiasbynens> | having one type of segmentation on String.prototype.* and two others on Intl.* would be weird |
18:59 | <ljharb> | right, i don't think a generic segmenter belongs on strings |
18:59 | <devsnek> | ljharb: wait do you want |
18:59 | <ljharb> | but like you can iterate on chars, and on code points, i want to be able to iterate on graphemes |
18:59 | <devsnek> | Intl.Segmenter on String.prototype or |
18:59 | <devsnek> | the code point iterator |
19:00 | <ljharb> | ie, `'a🏳️🌈c'` is 8 chars, 6 code points, but 3 graphemes |
19:00 | <ljharb> | so i want an iterator that gives me 3 things and not 6 or 8 |
19:01 | <ljharb> | i realize this is a subset of what Intl.Segmenter does, but it feels to me like the subset would make sense directly on String.prototype, mandated (unless that, too, is locale-dependent?) |
19:01 | <devsnek> | splitting by graphemes needs locale data right? |
19:01 | <devsnek> | like you'd bring in the icu lib |
19:02 | <ljharb> | maybe? what locale data does it need |
19:02 | <devsnek> | at which point you'd expose Intl.Segmenter anyway |
19:02 | <ljharb> | does the data needed for splitting on graphemes equal the data needed for all of Intl? |
19:02 | <ljharb> | i'd assume it's a small subset |
19:03 | <devsnek> | i mean that's true for each individual item we ship in Intl |
19:03 | <devsnek> | though some are larger than others |
19:04 | <ljharb> | mathiasbynens: can you explain how splitting grapheme clusters is locale-dependent? |
19:07 | <devsnek> | CLDR contains data for grapheme clustering |
19:08 | <ljharb> | right but i mean like, there's graphemes that aren't clustered consistently across locales? |
19:09 | <jridgewell> | https://unicode.org/reports/tr29/#Conformance |
19:10 | <jridgewell> | > For example, reliable detection of word boundaries in languages such as Thai, Lao, Chinese, or Japanese requires the use of dictionary lookup, analogous to English hyphenation. |
19:10 | <jridgewell> | It's not as simple as "does this char join with the previous char" |
19:10 | <jridgewell> | There's not a property on the Unicode char that we could look at. |
19:11 | <rkirsling> | word boundaries are tricky for sure, but ljharb is just concerned with graphemes |
19:11 | <ljharb> | right, i don't care about words |
19:13 | <jridgewell> | > These algorithms can be adapted to produce tailored grapheme clusters for specific locales or other customizations, such as the contractions used in collation tailoring tables. |
19:13 | <devsnek> | > The following is a general specification for grapheme cluster boundaries—language-specific rules in [CLDR] should be used where available. |
19:16 | <rkirsling> | stuff like 👨👩👧👦 is also an issue, dependent on env but not locale |
19:20 | <rkirsling> | https://mathiasbynens.be/notes/javascript-unicode#other-grapheme-clusters |
19:24 | <ljharb> | "For a completely accurate solution that works for all Unicode scripts, implement this algorithm in JavaScript, and then count each grapheme cluster as a single symbol" |
19:24 | <ljharb> | sounds like it's doable |
19:25 | <rkirsling> | oh here we go |
19:25 | <rkirsling> | jridgewell's last quote above precedes this table |
19:25 | <rkirsling> | https://unicode.org/reports/tr29/#Table_Sample_Grapheme_Clusters |
19:25 | <rkirsling> | the last chunk of the table is locale-specific |
19:26 | <ljharb> | so we could have it on string, and let 402 fill it in for the last chunk |
19:27 | <devsnek> | or you could just use Intl.Segmenter |
19:31 | <jridgewell> | https://www.irccloud.com/pastebin/TfliWeVm/simple-graphemes.js |
19:32 | <gibson042> | default rules for identifying grapheme cluster boundaries: http://unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules |
19:32 | <shu> | PSA: please add incubator call items at https://github.com/tc39/incubator-agendas/blob/master/2020/04-14.md |
19:32 | <shu> | empty so far, i plan to add realms by EOD, but would like to give other folks a chance to add their items first |
19:32 | <gibson042> | they depend upon character property data, but are not locale-specific |
19:32 | <devsnek> | jridgewell: that doesn't yield anything |
19:33 | <jridgewell> | Did for me |
19:33 | <rkirsling> | I mean, it is a fact that https://github.com/orling/grapheme-splitter/blob/master/index.js is intending to implement the standard, and is <1750 lines with no deps |
19:33 | <rkirsling> | and has no locale arg, I mean |
19:33 | <ljharb> | jridgewell: [...graphemes('🏳️🌈💩')]` gives me just a single white flag |
19:33 | <jridgewell> | Oh, because I had a `Mark` char in mine. |
19:34 | <mmarchini> | has there been any proposals so far to investigate improving ergonomics of `const` and `try/catch`? `let res; try { res = await fetch(url); } catch (e) { /* ... */ } /* res can be overriden */` |
19:35 | <devsnek> | mmarchini: ergonomics of statements in general |
19:35 | <ljharb> | mmarchini: do expressions |
19:35 | <devsnek> | yep |
19:35 | <ljharb> | mmarchini: https://github.com/tc39/proposal-do-expressions |
19:35 | <jridgewell> | http://unicode.org/reports/tr29/#Table_Combining_Char_Sequences_and_Grapheme_Clusters |
19:35 | <jridgewell> | Wanna code that table into a regex? |
19:36 | <ljharb> | jridgewell: did you just ask me to write a distributed map reduce function in erlang |
19:36 | <ljharb> | :-p |
19:36 | <mmarchini> | do expressions implicitly returns the last expression, right? |
19:36 | <jridgewell> | No, we have the ability to encode it with properties. |
19:36 | <jridgewell> | I just don't wanna do it. |
19:37 | <devsnek> | mmarchini: not exactly |
19:37 | <devsnek> | but more or less yes |
19:37 | <ljharb> | jridgewell: was referencing https://lh3.googleusercontent.com/proxy/Qx2opykPRGCvHcvcqdfCxygjKoFmE4ZXMKuUB0fWV2KB2NMnAoS_AGBlt4M0k99imlqraTw2k55_y8oSNkD3iSgcFl2Bu9PYnsh9YNjfrpU8OrXZWcgi1emZvQ |
19:37 | <devsnek> | mmarchini: they work the same as what eval() returns |
19:37 | <ljharb> | shu: what's the process for adding things to "chartered proposals"? |
19:37 | <rkirsling> | there's also the explicit resource mgmt proposal |
19:37 | <shu> | ljharb: you want to talk about another proposal? |
19:38 | <mmarchini> | so the above would look like `const res = do { let res; try { await res(url) } catch (e) { /* ... */ }; res}` |
19:38 | <mmarchini> | oh, I don't think I'm familiar with how eval returns |
19:38 | <devsnek> | mmarchini: you can get rid of the res variable |
19:39 | <shu> | ljharb: the current process is to wait until the next plenary. people said they didn't want to feel pressured to check on an agenda every 2 weeks, so the proposed process was folks are free to call out proposals they think should be discussed in the incubator meetings in between two plenaries |
19:39 | <devsnek> | `do { try { x } catch { y } }` |
19:39 | <ljharb> | shu: i don't have anything in mind, just was curious |
19:39 | <mmarchini> | `const res = do { try { res(url) } catch (e) { console.error(e); undefined }}`? |
19:39 | <rkirsling> | shu: oh I missed that point, that's good to know |
19:39 | <shu> | ljharb: and so the stakeholders would be able to get a heads up and agree to participating in the call until the next plenary |
19:39 | <ljharb> | mmarchini: console.error already returns undefined |
19:39 | <ljharb> | shu: gotcha |
19:39 | <mmarchini> | right |
19:39 | <devsnek> | at that point |
19:40 | <mmarchini> | `const res = do { try { res(url) } catch (e) { undefined }}` |
19:40 | <mmarchini> | oops |
19:40 | <mmarchini> | `const res = do { try { res(url) } catch (e) { console.error(e) }}` |
19:40 | <mmarchini> | hum |
19:40 | <mmarchini> | interesting |
19:40 | <devsnek> | i'd do `await fetch().catch((e) => console.error(e))` |
19:40 | <mmarchini> | a little verbose, bot not bad |
19:40 | <mmarchini> | but* |
19:40 | <mmarchini> | gotcha |
19:41 | <ljharb> | indeed, devsnek's is better :-) |
19:41 | <ljharb> | do expressions are super useful but a lot of the examples could be easily reworked to be better, and not need do expressions in the first place |
19:42 | <rkirsling> | there's also https://github.com/tc39/proposal-explicit-resource-management for `try (const ...) {}` though |
19:42 | <rkirsling> | oh wait my bad |
19:42 | <ljharb> | that doesn't make the variable available outside the try |
19:42 | <rkirsling> | yeah |
19:42 | <rkirsling> | available outside with const, was the goal |
19:42 | <rkirsling> | sorry |
19:42 | <devsnek> | i really want do expressions :( |
19:42 | <ljharb> | same |
19:42 | <rkirsling> | ditto |
19:43 | <devsnek> | as long as they allow return and break and stuff that is |
19:43 | <devsnek> | without those they're useless to me |
19:44 | <ljharb> | oh, i want them to not have those things |
19:44 | <devsnek> | it doesn't even make sense for them to not have those |
19:44 | <ljharb> | it doesn't make sense to me to have an expression able to affect control flow (beyond throwing) |
19:44 | <devsnek> | it's a block |
19:44 | <devsnek> | that you can get a value out of |
19:45 | <ljharb> | right, the latter imo trumps the former |
19:45 | <ljharb> | things you can get a value out of, can't affect control flow, modulo exceptions |
19:45 | <devsnek> | i don't understand how "get a value out of" has anything to do with scope |
19:45 | <mmarchini> | I find do expressions very confusing without explicitly return |
19:45 | <mmarchini> | explicit* |
19:46 | <mmarchini> | "it behaves like eval" will not be a valid explanation for most JS developers |
19:46 | <ljharb> | i think it's a valuable thing to know that `return`, for example, can't occur in expression position |
19:46 | <mmarchini> | s/valid/didactic/ |
19:46 | <devsnek> | i'm just imagining how useless block expressions would be in rust if you couldn't break/return out of them |
19:46 | <ljharb> | devsnek: what are your use cases for do expressions that need these? |
19:46 | <devsnek> | that i want to return from a function or break a loop |
19:46 | <ljharb> | right but what would the code be |
19:46 | <devsnek> | it doesn't matter |
19:47 | <mmarchini> | it doesn't have to be `return`, but making explicit in the expression what value is getting out would be important IMO |
19:47 | <ljharb> | it definitely matters - i can't conceive of when you'd need to return, or break, but also extract a value out of the code |
19:47 | <ljharb> | mmarchini: it couldn't be `return` due to confusion; but syntactically marking the value to get out would probably be tricky |
19:47 | <devsnek> | i wish github had better code search |
19:47 | <devsnek> | you should go look at rust code that uses block expressions |
19:48 | <ljharb> | different languages have different common use cases; i'd want an example in JS where you'd use this |
19:48 | <devsnek> | i have some logic |
19:48 | <devsnek> | inside a block |
19:48 | <devsnek> | that necessitates returning |
19:48 | <ljharb> | then why do you need to capture the value of the block |
19:48 | <mmarchini> | ljharb I think I get where you coming from: those are expressions and not blocks |
19:48 | <devsnek> | because its conditional or smth idk |
19:49 | <ljharb> | right, it's "do expressions" not "do blocks" |
19:49 | <ljharb> | devsnek: lol well if you don't know then how is it a use case |
19:49 | <mmarchini> | so maybe the bracked syntax is what's confusing me (and I expect it would confuse other folks as well) |
19:49 | <devsnek> | ljharb: i have specific code i would like to write |
19:49 | <devsnek> | but i don't think that's important |
19:49 | <mmarchini> | and in this case try/catch and if/else shouldn't be allowed, right? since we're avoiding any kind of control flow |
19:49 | <devsnek> | what |
19:50 | <devsnek> | y'all seem to really be missing the point |
19:50 | <mmarchini> | (not saying I'm agreeing with it) |
19:50 | <ljharb> | devsnek: without code, how can we avoid missing it |
19:50 | <ljharb> | mmarchini: nah, exceptions are fine |
19:50 | <rkirsling> | the real goal of do exprs is the wish that we could have "everything is an expression" in JS |
19:50 | <ljharb> | mmarchini: expressions can still throw |
19:50 | <ljharb> | rkirsling: but, explicitly instead of implicitly |
19:50 | <ljharb> | rkirsling: i actively don't want implicit expressions everywhere, i *like* the statement/expression dichotomy, and wish functions aren't both |
19:51 | <devsnek> | ljharb: why do you not believe i would want to use return inside a block |
19:51 | <devsnek> | i |
19:51 | <devsnek> | i'm sure you've used return inside blocks before |
19:51 | <ljharb> | devsnek: i use return inside blocks all the time, that's not the issue |
19:51 | <ljharb> | devsnek: i'm not understanding why you'd do that *and* want to capture the value of the block |
19:51 | <rkirsling> | ah okay. I would just as well not have statements in a language myself, but regardless, implicit would be quite a language revamp |
19:51 | <devsnek> | because both are useful |
19:51 | <devsnek> | orthagonally |
19:51 | <rkirsling> | therefore `do { ... }` is explicit |
19:51 | <ljharb> | devsnek: i'm not disputing it, i'm asking for a single non-contrived concrete example of it being useful |
19:52 | <devsnek> | ljharb: expanding my macros in engine262 |
19:52 | <ljharb> | can you link me to an example? |
19:52 | <jridgewell> | ljharb: because capturing the value of the block is the entire point |
19:52 | <devsnek> | https://github.com/engine262/engine262/blob/master/scripts/transform.js |
19:53 | <rkirsling> | but I would think that that means that `do` just means putting a _scope_ around "everything is an expression" and not restricting what (would-be) statements are allowed in there |
19:53 | <devsnek> | like `do if () {}`? |
19:53 | <jridgewell> | https://www.irccloud.com/pastebin/2hSLTTCi/do-expression-return.js |
19:54 | <jridgewell> | You can't always devolve an the remaining part of a block into an else condition. |
19:54 | <rkirsling> | devsnek: tbf `do if` is just somebody complaining about ternaries |
19:54 | <jridgewell> | rkirsling: ternaries don't allow variable delcarations |
19:55 | <devsnek> | jridgewell: everything after an if+return is implicitly the else bock |
19:55 | <bradleymeck> | do with return gets really weird/new |
19:55 | <devsnek> | block |
19:55 | <jridgewell> | Blocks are necessary to cleanly express a lot of code |
19:55 | <jridgewell> | devsnek: You can't always devolve an the remaining part of a block into an else condition. |
19:55 | <bradleymeck> | i think its a good idea, but doubt we should use the `do` keyword due to how gross the `do...while` interactiom is |
19:55 | <devsnek> | why can't i refactor any `let x; <some statement or block>` to `let x = do { <some statement or block> }` |
19:56 | <jridgewell> | My code example is contrived, and is just meant to demonstrate early return |
19:56 | <ljharb> | jridgewell: ok so can you give me an example of when you *can't* devolve the rest of the block into an else? |
19:56 | <devsnek> | jridgewell: you want to early return to the end of the do block? |
19:56 | <ljharb> | jridgewell: i'm not saying "there are none", i'm saying "please give me one because nobody seems to be capable of doing that so far" |
19:56 | <devsnek> | i've never not been able to devolve into an if+else in rust |
19:56 | <jridgewell> | At the beginning |
19:56 | <rkirsling> | jridgewell: to be clear I meant `do if` as opposed to `do { if {} }` |
19:56 | <devsnek> | jridgewell: i've never encountered something that can't be |
19:57 | <jridgewell> | const x = do { |
19:57 | <devsnek> | ? |
19:58 | <jridgewell> | This can't be cleanly develoved into an if-else. https://www.irccloud.com/pastebin/bmg0madZ/do-expressions.js |
19:58 | <jridgewell> | Again, contrived. |
19:58 | <jridgewell> | But every set if conditions can't be cleanly devloved into an if-else. |
19:58 | <devsnek> | it can be |
19:58 | <jridgewell> | But not** |
19:58 | <devsnek> | it would take a few minutes of thinking |
19:59 | <jridgewell> | **cleanly** |
19:59 | <devsnek> | and i'd argue that's spaghetti with or without do expressions |
19:59 | <jridgewell> | I don't want to repeat `doSomething()` |
19:59 | <jridgewell> | It's a fallthrough, which is very valuable. |
20:00 | <ljharb> | jridgewell: sure it can `if (first) { if (second) { something; } else { doSomething(); } } else { doSomethingMore(); value; }` |
20:00 | <ljharb> | and then you return that entire thing wrapped in `do { }` |
20:01 | <jridgewell> | Nit picking my example isn't really the point. |
20:01 | <ljharb> | i'm still seeing some very smart people struggling to come up with a single non-contrived concrete example of where it'd be useful to have return/break/continue inside a do expression, which doesn't bode well for the persuasiveness of the argument |
20:01 | <bradleymeck> | i think assigning to a var might be part of the issue with this basis, putting them inside awkward expression positions feels more concrete: `foo({x: existing ?? do { try { /*expensive*/ } catch { /* better message here */ } })` |
20:01 | <jridgewell> | Do this with a for-loop, where you early return. |
20:01 | <devsnek> | jridgewell: i've literally only ever seen logic that contrived in my js lexer |
20:02 | <ljharb> | bradleymeck: that example is fine, there's no return/break/continue in it |
20:02 | <bradleymeck> | ljharb: i mean i can make a return based example in there |
20:02 | <devsnek> | y'all should really try using some languages with this behaviour |
20:02 | <bradleymeck> | such as things that fail silently |
20:02 | <ljharb> | bradleymeck: i'd love to see my first non-contrived one |
20:02 | <bradleymeck> | i don't think contrived is a valuable position |
20:03 | <bradleymeck> | we have a turing complete language, everything is contrived |
20:03 | <devsnek> | ljharb: it occurs to me you can have a no-return-in-do-expression eslint rule |
20:04 | <bradleymeck> | i'd lean on allowing usages rather than trying to pin the language down to only having 1 way to accomplish anything |
20:04 | <ljharb> | devsnek: sure, but why add burden for those that lint if there's not a compelling argument to allow it in the first place |
20:04 | <devsnek> | well we shouldn't have 20 ways to accomplish something, but you should be able to compose features into new ways of using them |
20:05 | <devsnek> | ljharb: i would use it |
20:05 | <devsnek> | i feel like 95% of my arguments turn into people saying my use cases aren't valid |
20:05 | <ljharb> | devsnek: and all i've been asking for is, to see some JS code where you'd use it :-/ |
20:05 | <devsnek> | ljharb: i linked you to an example |
20:05 | <ljharb> | i'm not saying your use cases aren't valid, i'm saying "please give me > 0 use cases" |
20:05 | <bradleymeck> | `for (...) { results.push(do { try { ... } catch { continue } }) }` |
20:05 | <ljharb> | devsnek: in JS? or in rust |
20:05 | <devsnek> | that transform code isn't even feature complete |
20:05 | <devsnek> | i linked you some js |
20:06 | <ljharb> | oh, let me take a look |
20:06 | <devsnek> | bradleymeck: more generally, wherever you'd normally want to express control flow, you might want to do so if that logic happens to occur inside a do expression |
20:06 | <ljharb> | devsnek: any lines in particular? |
20:07 | <devsnek> | ljharb: the entire thing is a system to restructure random expressions into using control flow |
20:07 | <bradleymeck> | devsnek: i mean thats the complaint with arr.forEach in general vs loops |
20:07 | <devsnek> | and like i said its not complete, there are certain ways you can't use macros in engine262 because i haven't figured out how to transform them correctly |
20:07 | <bradleymeck> | the only thing with do expressions is they can live in an expression position |
20:09 | <bradleymeck> | so, things that want to skip an effect while being in an expression position such as the .push example above are prime examples |
20:09 | <devsnek> | the argument i did find interesting was that someone would be confused about return inside a do expression and whether it returned the block or the enclosing function |
20:09 | <devsnek> | but i think that's more of a general language education problem |
20:09 | <bradleymeck> | you could refactor things into a lot of statements, or you could keep the bailout logic in the literal 🤷 |
20:10 | <bradleymeck> | for what it is worth, i think return/continue/break are scary, but have no clear reason to argue against them as you could refactor things to be done w/o do expressions anyway; that just makes it more verbose / may increase loss of locality |
20:11 | <bradleymeck> | labelled break/continue are super interesting here |
20:11 | <devsnek> | labels are illegal |
20:11 | <bradleymeck> | in the proposal? we have power to alter such things |
20:11 | <devsnek> | no i meant in general, it was a joke |
20:12 | <bradleymeck> | i use labels! |
20:12 | <devsnek> | i've only used labels once |
20:12 | <devsnek> | for lexing whitespace in js |
20:12 | <devsnek> | nested loop sadness |
20:12 | <bradleymeck> | i use em in bad places |
20:12 | <bradleymeck> | like_here: debugger; |
20:12 | <devsnek> | why do you label a debugger statement |
20:13 | <bradleymeck> | gives me my debugger operands proposal effectively /cackling |
20:13 | <devsnek> | wait i don't think that syntax is even valid |
20:13 | <devsnek> | LabelledStatement and DebuggerStatement are both productions of Statement directly |
20:13 | <bradleymeck> | browsers think it is |
20:14 | <devsnek> | wait a second |
20:14 | <devsnek> | LabelledItem is any statement |
20:14 | <devsnek> | this changes everything |
20:14 | <bradleymeck> | yes, they can even nest |
20:14 | <bradleymeck> | if i remember |
20:15 | <bradleymeck> | what does it change? |
20:16 | <bradleymeck> | devsnek: are you just gonna add a ton of labels? |
20:16 | <devsnek> | lol it doesn't actually change much |
20:16 | <devsnek> | i just never realized you could label things that aren't loops |
20:16 | <bradleymeck> | i thought you were about to do something weird like |
20:17 | <bradleymeck> | return_if_abrupt: foo(); |
20:17 | <ljharb> | you can break to a label without a loop as well, i think |
20:17 | <bradleymeck> | yea |
20:18 | <devsnek> | aw i was hoping this would work `x: { console.log('hi!'); while (true) { continue x; } }` |
20:19 | <bradleymeck> | nah, just break |
20:19 | <devsnek> | also TIL break is not constrained to loops |
20:19 | <devsnek> | what a wild world |
20:20 | <bradleymeck> | why would it be? |
20:20 | <devsnek> | what are you breaking if you aren't in a loop |
20:21 | <ljharb> | the quality of your code |
20:21 | <devsnek> | lol |
20:21 | <devsnek> | `x: { console.log('hi!'); break x; console.log('bye') }` |
20:21 | <devsnek> | so this doesn't log bye |
20:22 | <devsnek> | you could use that for early return in do expressions |
20:28 | <bradleymeck> | that isn't the same, the completion value would carry over rather than stop outer scope |
20:30 | <devsnek> | jridgewell's example https://gc.gy/53996426.png |
20:31 | <ljharb> | devsnek: fwiw i don't mind anything that breaks out of the do expression; but that you could put the label anywhere seems like it'd be bad |
20:31 | <devsnek> | wdym |
20:31 | <ljharb> | devsnek: so like, i'd be ok with a do-expression-level unlabelled break |
20:31 | <devsnek> | unlabelled break has to go to the enclosing loop |
20:31 | <ljharb> | not if it's inside a do expression |
20:32 | <ljharb> | (you can make a consistency argument that it must, sure, but i'm saying it could have different semantics there) |
20:32 | <devsnek> | why do things have to randomly be different inside the block of a do expression |
20:33 | <ljharb> | things are already that way, because of the completion value |
20:33 | <devsnek> | it's not a problem to use if/else in rust |
20:33 | <ljharb> | no other blocks are expresisons |
20:34 | <rkirsling> | wait, was the statement about `break` just in the context of do exprs? |
20:34 | <ljharb> | rkirsling: which statement |
20:34 | <rkirsling> | the idea that you could use it as goto in general |
20:34 | <ljharb> | no, you can already do that |
20:34 | <rkirsling> | how? |
20:34 | <ljharb> | outside of a loop, you can't use an unlabelled break; but you can use a labelled break anywhere |
20:35 | <devsnek> | you can't use it as goto |
20:35 | <rkirsling> | that's deeply horrifying but also I can't get that to happen in eshost whatsoever |
20:35 | <devsnek> | https://gc.gy/53996740.png |
20:35 | <ljharb> | oh hm, maybe it's only out of blocks |
20:36 | <devsnek> | you can't jump |
20:36 | <rkirsling> | ahh if it's just "break out of block" I'm less horrified |
20:36 | <ljharb> | `(function () { x: console.log('a'); break x; }())` says "undefined label x" |
20:36 | <ljharb> | ok yeah same |
20:36 | <rkirsling> | phew |
20:37 | <devsnek> | anyway |
20:37 | <devsnek> | people who are concerned about early exit from do expressions |
20:37 | <devsnek> | should try out languages which have blocks as expressions |
20:37 | <devsnek> | because its basically not a problem |
20:37 | <devsnek> | like every once in a while you have to refit something to be if/else but it's very trivial |
20:38 | <gibson042> | ECMAScript allows expressions in places like variable initializers |
20:39 | <devsnek> | it allows them in lots of places |
20:39 | <devsnek> | almost anywhere in fact |
20:51 | <gibson042> | more specifically, ECMAScript allows expressions in places where it _doesn't_ allow control statements |
20:52 | <devsnek> | it allows them in places where it doesn't allow statements |
20:52 | <devsnek> | control or otherwise |
20:52 | <gibson042> | a pathological example from an earlier meeting was something like `function foo(a = do { return }){}` |
20:52 | <ljharb> | ^ that is something that imo shouldn't be possible |
20:53 | <devsnek> | 🤷🏻 https://gc.gy/53997804.png |
20:53 | <devsnek> | seems well defined enough |
20:53 | <devsnek> | i'd expect a no-do-in-arg-init eslint rule regardless of whether they can contain control statements |
20:54 | <ljharb> | perhaps, but i wouldn't enable it |
20:54 | <rkirsling> | why not |
20:54 | <devsnek> | why shouldn't it be possible |
20:55 | <devsnek> | is what i'm curious about |
20:55 | <ljharb> | rkirsling: i wouldn't enable it because i don't think there's a problem with using a do expression as a default argument - in place of the places that might currently invoke a function instead |
20:55 | <devsnek> | well actually i know why it shouldn't be possible |
20:55 | <ljharb> | devsnek: in general, i lean towards all things should be prohibited unless its good usage outweighs its bad usage |
20:56 | <gibson042> | now do `function foo(a = do { continue }){}` |
20:56 | <devsnek> | it shouldn't be possible because arg init shouldn't be +Return |
20:56 | <bradleymeck> | i already have some "do" via iife in default arge for throwing |
20:56 | <devsnek> | gibson042: arg init can't contain an outer loop |
20:56 | <devsnek> | it's at the function boundary |
20:56 | <devsnek> | that's always an unsyntactic continue |
20:57 | <devsnek> | gibson042: the key here is that function arg init shouldn't be +Return, regardless of whether do expressions exist or not |
20:57 | <gibson042> | agreed |
21:01 | <Bakkot> | breaking non-loops is useful in almost exactly the set of cases when an early return is useful, it's just that usually you would refactor that code to use a function with an early return rather than keeping it inline. but that's not always something you want to do. |
21:02 | <devsnek> | yeah i pointed that out above with jridgewell's example |
21:04 | <Bakkot> | most of the places i've ended up emitting a non-loop with a label are in generated code |
21:04 | <Bakkot> | ljharb: re: grapheme clusters being locale-dependent, the example which always occurs to me is, there are some flags which correspond to entities whose status as "a state" (and therefore which qualify for a flag) is a matter not everyone agrees upon |
21:04 | <devsnek> | i've never even seen that in generated code |
21:05 | <devsnek> | would love to see some examples |
21:05 | <Bakkot> | which means flag emoji +ZWJ+their country code may, or may not, be a single character |
21:05 | <Bakkot> | devsnek: code I'm generating tends to be proprietary, alas |
21:05 | <devsnek> | oh this is code *you're** generating |
21:06 | <Bakkot> | well, it is code which tools I wrote are generating, at any rate |
21:06 | <ljharb> | Bakkot: oh meaning like taiwan, or palestine, to name the most controversial examples i can think of? |
21:06 | <devsnek> | i'm curious what higher-level control flow mapps to that output |
21:06 | <Bakkot> | ljharb I was extremely carefully in not naming examples :P |
21:06 | <Bakkot> | but yes |
21:07 | <ljharb> | Bakkot: that makes sense, but it still seems like something that could be addressed in a generic way (but Intl/locales could decide) |
21:08 | <gibson042> | Bakkot: I thought all the regional flags used regional indicators, which are always grapheme clusters per the default rules |
21:08 | <Bakkot> | devsnek the higher level control flow is usually functions which have early returns which I am inling |
21:08 | <devsnek> | i see |
21:09 | <Bakkot> | gibson042 the regional indicators may or may not be joined into a single unit depending on whether the code you're running recognizes that falg |
21:09 | <gibson042> | default rules include "Do not break within emoji modifier sequences or emoji zwj sequences" and "do not break between regional indicator (RI) symbols if there is an odd number of RI characters before the break point" |
21:10 | <Bakkot> | it's not breaking between the RI symbols, is whether you end up with just a colored flag (one grapheme), or a white flag + a regional indicator (two graphemes) |
21:10 | <devsnek> | now i kind of want to write a babel transform that does function inlining using labels |
21:11 | <gibson042> | that presentation detail is not relevant to grapheme cluster boundaries |
21:11 | <Bakkot> | I suspect it is relevant to the thing ljharb wants, though |
21:16 | <ljharb> | i think i'd be content with the default 262 impl being the default rules, and 402 imposing the locale-dependent ones on top of it |
21:16 | <ljharb> | (assuming it would do the "right" thing in most cases) |
21:21 | <rkirsling> | would be interested in hearing from engines that have no intention to implement 402 though |
21:21 | <gibson042> | if there were commitment to put it in ECMA-262, that would seem to be the best approach (specify default rules and let ECMA-402 override, similar to but better than how ECMA-262 specifies toLocale*) |
21:22 | <gibson042> | as of today, the default rules appear to require no information not already required of regex \p |
21:22 | <ljharb> | rkirsling: you can compile node to node include intl, and i don't think xs does |
21:22 | <ljharb> | *to not include |
21:22 | <ljharb> | rkirsling: node < 12 or 13 didn't include it by default, iirc |
21:22 | <rkirsling> | yeah XS doesn't, is a key example |
21:22 | <ljharb> | gibson042: thanks, that sounds good |
21:23 | <rkirsling> | my point is that engines that do implement 402 have ICU to use, even for a feature that might be 262-side |
21:23 | <rkirsling> | but XS would have to reimplement and maintain this logic themselves |
21:24 | <ljharb> | right |
21:31 | <gibson042> | but I don't know if it makes sense or not... grapheme cluster segmentation seems to occupy a gray area between code unit/code point segmentation (trivial, deterministic, time-invariant) and word/sentence segmentation (difficult, locale-dependent, and time-dependent) |
21:33 | <rkirsling> | yeah :-/ |
21:34 | <rkirsling> | regex \p is a good point though, I wonder whether XS would opt out of that too |
21:38 | <rkirsling> | (I mean, they are currently opting out, but the question is would they ever change that) |
21:39 | <ljharb> | in the fullness of time, they'd have to implement anything that wasn't normative optional, no? |
21:41 | <rkirsling> | in theory, though I feel like that's kind of a new concept in 262 outside of Annex B? |
21:41 | <ljharb> | well, function toString returning source is normative optional via the host hook :-p |
21:42 | <rkirsling> | I guess I just mean that that phrase occurs in 402 but not in 262 at present |
21:43 | <ljharb> | oh, sure |
21:44 | <ljharb> | but i doubt they'd be able to get away with not implementing regex `\p` and still be 262-compliant |
21:46 | <rkirsling> | certainly, but they consciously ship with caveats: https://github.com/Moddable-OpenSource/moddable/blob/public/documentation/xs/XS%20Conformance.md#caveat |
22:06 | <Bakkot> | ljharb: backing up a step, I feel like grapheme segmentation is a thing I am unlikely to care about on platforms which lack Intl; is there a specific reason you care about having it on non-Intl platforms? |
22:11 | <ljharb> | i don't feel like i can rely on Intl in any of my code, because i want my code to be as portable as possible. i don't have a concrete use case for this right now, it's not a proposal :-) just asking out loud |
22:20 | <rkirsling> | it's a worthy discussion, but I'm not sure a portability argument holds water, if the places where it wouldn't work (in the long-term) are just memory-constrained places like microcontrollers |
22:20 | <rkirsling> | I don't think other engines are opting out so much as deprioritizing it? |
22:21 | <rkirsling> | (btw I'm removing JSC's ENABLE_INTL define as we speak 😉) |
22:30 | <shu> | removing it so it's always on? |
22:31 | <devsnek> | removing it so its always off |
22:31 | <shu> | sad trombone noises |
22:31 | <rkirsling> | on! |
22:31 | <shu> | happy trumpet noises |
22:31 | <rkirsling> | wherefore this disinformation |
22:31 | <devsnek> | lol |
22:31 | <devsnek> | now if we can just convince v8 to always ship intl |
22:32 | <rkirsling> | gonna assume the Intl object exists and then we can just runtime-guard new classes |
22:32 | <rkirsling> | (there was only one upstream platform flipping it off, is why this should be okay to simply do as maintenance) |