00:24 | <rkirsling> | (whining aside though, it'll probably be beneficial to have a more structured schedule for a bit, lol) |
02:09 | <leobalter> | devsnek: congrats! Don’t stress out to much, it will probably be better to focus on the onboarding and take some rest if necessary instead of much of TC39 stuff. Don’t let the FOMO take out your energy. |
14:47 | <Bakkot> | morning all |
14:51 | <rkirsling> | mornin' |
14:55 | <bterlson> | TCQ: https://tcq.app/meeting/KfmX |
14:56 | <chicoxyzzy> | meeting info link in the topic is outdated. should be https://github.com/tc39/Reflector/issues/279 |
14:58 | <chicoxyzzy> | thank you Rob :) |
14:59 | <mathiasbynens> | did anyone create notes docs already? happy to update the short URLs again |
15:01 | <Bakkot> | mathiasbynens they're in the reflector, yeah |
15:01 | <Bakkot> | also reminder to all not to link the notes here, in this publicly logged channel |
15:02 | <mathiasbynens> | i don't see them in https://github.com/tc39/Reflector/issues/279 |
15:02 | <mathiasbynens> | i see, #294 |
15:06 | <michaelficarra> | good morning everyone :-) |
15:08 | <howdoi> | Good morning! :) |
15:08 | <littledan> | if you call on me next, I can give my standard IPR disclaimer |
15:08 | <littledan> | I imagine Istvan's thing is not what was being asked about |
15:09 | <littledan> | akirose: ^ |
15:09 | <mpcsh> | akirose: your headphones 😂 |
15:09 | <mpcsh> | so fetch |
15:09 | <rkirsling> | +1 |
15:10 | <rkirsling> | littledan: you kind of look like you're interrogating us with that light :p |
15:10 | <littledan> | heh I don't have my awesome coworking space setup |
15:10 | <littledan> | my partner is planning on building a nice planter behind me and hanging up some of his art so it can show up in my video calls, but I guess we didn't plan for the light |
15:11 | <rkirsling> | that sounds nice though :) |
15:13 | <rkirsling> | did you just say mancy |
15:13 | <rickbutton> | mancy |
15:36 | <shu> | i'm confused about this slides sharing |
15:36 | <shu> | *i* can control the slide? |
15:36 | <Bakkot> | oh no |
15:37 | <rricard> | deep powerpoint integration probably |
15:37 | <Bakkot> | that is too much power |
15:37 | <shu> | wait when i'm jumping around |
15:37 | <shu> | am i making it jump around for other viewers, or just myself |
15:37 | <rricard> | it's not jumping for us |
15:37 | <shu> | ok |
15:37 | <shu> | whew |
15:37 | <rricard> | you can peek ahead |
15:38 | <rickbutton> | I assume that doesn't work for Google Slides |
15:38 | <rricard> | I would assume that yes |
15:39 | <rricard> | you can always export your slides in ppt |
15:40 | <Bakkot> | presumably one would need to actually have ppt lying around for that to do you any good |
15:40 | <Bakkot> | I don't think I have used a machine with ppt since high school |
15:41 | <Bakkot> | oh, teams is a ppt viewer apparently, neat |
15:45 | <akirose> | fwiw i literally just uploaded Istvan's ppt that he had emailed to the chair straight to the teams client |
15:45 | <akirose> | no ppt.app launched |
15:46 | <ljharb> | phew, irc is working again |
15:46 | <ljharb> | (for me) |
15:48 | <akirose> | draft schedule link added to reflector#279 |
15:54 | <littledan> | these editorial improvements are awesome! |
15:55 | <marja_> | +1 |
15:56 | <rkirsling> | +1 |
16:01 | <michaelficarra> | thanks littledan :-) |
16:23 | <littledan> | akirose: *green heart emoji* been feeling similarly |
16:23 | <rickbutton> | 100000% |
16:26 | <devsnek> | is tcq showing everything or just stuff for today |
16:26 | <robpalme> | everything |
16:26 | <robpalme> | we chew through as much of it as we can |
16:27 | <robpalme> | it may not yet be fully populated but that is the intent |
16:27 | <devsnek> | cool |
16:27 | <devsnek> | do we have that hackmd schedule this time around |
16:28 | <Bakkot> | this channel is public and should not have the notes doc linked in it |
16:28 | <ryzokuken> | devsnek: we do |
16:28 | <Bakkot> | robpalme ^ |
16:28 | <rickbutton> | devsnek: the hackmd is on the reflector issue for the meeting, its at the top, "Draft Schedule" |
16:29 | <ljharb> | robpalme: Bakkot should we move today's notes to another URL? |
16:29 | <Bakkot> | ehhhh it's probably not worth worrying about |
16:29 | <ystartsev> | i can move it |
16:29 | <ystartsev> | either at lunch or eod |
16:30 | <devsnek> | thanks |
16:30 | <ystartsev> | any preference? |
16:34 | <littledan> | yeah, I agree that late agenda items should be deprioritized |
16:35 | <ryzokuken> | Isn't today a holiday in parts of Europe? |
16:35 | <leobalter> | ryzokuken: it is |
16:36 | <ryzokuken> | that's likely the issue, I guess 😅 |
16:44 | <Bakkot> | jridgewell I am glad/impressed you know those NamedEvaluation details off the top of your head |
16:45 | <Bakkot> | I just reviewed this change and I still don't |
16:45 | <rkirsling> | note that ease for polyfill doesn't correspond to ease for impl |
16:45 | <ljharb> | shu: to clarify, chrome is shipping it with or without the inferred name? |
16:45 | <shu> | ljharb: without currently, because it's not in the spec |
16:45 | <ljharb> | thanks |
16:45 | <shu> | ljharb: if it is added, there is plenty of time to add it in before it hits stable |
16:46 | <ljharb> | gotcha |
16:46 | <ljharb> | drousso: you had thoughts on the issue thread btw |
16:46 | <drousso> | ya |
16:46 | <drousso> | i don't feel strongly |
16:47 | <drousso> | i am slightly towards "no" because of the short circuiting behavior |
16:47 | <Bakkot> | short circuiting behavior actually leans towards yes, I think |
16:48 | <Bakkot> | because with short circuiting it desugars to `a && a = function(){}`, which gets named |
16:48 | <rkirsling> | the tricky part is that while that is the desugaring, it's not the current code you would write |
16:48 | <ljharb> | or `if (a) { a = function () {} }` which also gets named |
16:51 | <drousso> | +1 to what rkirsling |
16:51 | <drousso> | said |
16:51 | <littledan> | personally, I don't see the size of the Babel output, or the desugaring, as a very strong argument one way or another |
16:51 | <littledan> | it's more about the general pattern that, in these sort of direct syntax assignment cases ending in = or :, you do name assignment |
16:51 | <littledan> | (sorry for caucusing before...) |
16:52 | <Bakkot> | yeah, agreed with littledan |
16:53 | <littledan> | sorry, the conclusion was yes, go ahead with named evaluation? |
16:54 | <ljharb> | sounds like yes, infer the name, as long as implementors can commit to implementing that |
17:08 | <Bakkot> | petition to consider shorter lunch breaks the next three days |
17:09 | <ljharb> | esp considering we're not all eating lunch at the same times when remote |
17:09 | <Bakkot> | I just had breakfast, so I'm not going to eat lunch, so I'm just sitting around |
17:16 | <devsnek> | is iterator helpers happening now |
17:16 | <jridgewell> | It's a tech check |
17:16 | <jridgewell> | We're on break till the new hour |
17:17 | <devsnek> | ah ok |
17:18 | <robpalme> | ***Note-takers and Note-readers*** Today's notes have been migrated. Please find the new link here: https://github.com/tc39/Reflector/issues/279 |
17:29 | <akirose> | i'm gonna mention this when we reconvene, but… we have way, way more time on the agenda than we have meeting time. use that information as you will. |
17:33 | <rkirsling> | folks in the breakout room feel like a one-hour lunch break doesn't really make sense under all-remote conditions |
17:36 | <bterlson> | rkirsling: as in, it's too long? |
17:37 | <rkirsling> | yeah |
17:38 | <rkirsling> | as in, like, even if you're in the appropriate time zone you might not need that long of a block of time when you're connected remotely anyway |
17:38 | <bterlson> | I dunno, I just finished cooking lunch, don't have that much time to eat :-P |
17:40 | <jridgewell> | The difference being it's ok to mute yourself and eat as presentations are going on |
17:40 | <jridgewell> | It's a little different than when we're in person |
17:40 | <shu> | shorter break time sgtm |
17:41 | <jridgewell> | Also the side discussions are much louder in-person, so having a long lunch break was nice |
17:50 | <ystartsev> | michaelficarra: are you around? |
17:56 | <sffc> | Is anyone here well-connected enough to get us an invitation to Clubhouse for the hallway track? https://www.wired.com/story/what-is-clubhouse-why-does-silicon-valley-care/ |
17:58 | <akirose> | "a new social network more exclusive than Berghain" AHAHAHAHAHAHAHAH |
17:58 | <robpalme> | we are restarting in 1 minute! |
18:02 | <robpalme> | starting now! |
18:04 | <devsnek> | if there are questions about iterator helpers stuff i can address i'm watching the channel here but not actively listening to the call |
18:07 | <Bakkot> | ppt integration seems much worse than the share-screen integration |
18:11 | <ryzokuken> | akirose: `Intl.DurationFormat` is today according to https://hackmd.io/@tc39-chairs/rylG45f2L#1300-CDT, but it's not on the agenda. |
18:11 | <ystartsev> | yep.. |
18:12 | <michaelficarra> | these slides are really good |
18:13 | <rbuckton> | Upstream iterator was never started either though |
18:13 | <littledan> | enclosing finally blocks around the current yield pause point are called by generator return FWIW |
18:14 | <ljharb> | eesh, i didn't realize the slides were advanced, i was stuck on slide 5. guess i'll open the link myself |
18:14 | <Bakkot> | iterators don't get started |
18:14 | <Bakkot> | generators get started |
18:14 | <Bakkot> | ... I think |
18:14 | <devsnek> | iterator is just a protocol |
18:14 | <devsnek> | there's no behaviour |
18:14 | <Bakkot> | but the protocol doesn't involve a "start" phase, I think |
18:14 | <devsnek> | right |
18:16 | <rbuckton> | Sure, but depending on .return to close upstream iterators isn't guaranteed either |
18:16 | <rbuckton> | s/close/"return" |
18:17 | <Bakkot> | not guaranteed, but it seems very strange to have a builtin which unconditionally prevents you from doing that |
18:17 | <Bakkot> | rather than deferring to the upstream |
18:18 | <littledan> | Bakkot: Well, you might think of the first .next() call is more or less a "start" phase |
18:20 | <akirose> | ryzokuken: working on it. bterlson & robpalme are debugging something |
18:20 | <Bakkot> | littledan: sure, but I wouldn't really expect that |
18:20 | <Bakkot> | you can call .return before .next |
18:20 | <Bakkot> | so it is reasonable to implement an iterator which is opened when it is obtained |
18:20 | <Bakkot> | not when it is first queried |
18:20 | <littledan> | right, this case is weird |
18:20 | <Bakkot> | and the builtins should work with that |
18:20 | <ryzokuken> | akirose: thanks. |
18:21 | <littledan> | (I wouldn't expect to have .return/.throw at all, and have previously proposed that we remove all that from the iterator protocol, but maybe it's too late now) |
18:21 | <devsnek> | there are many more slides |
18:21 | <rbuckton> | To me its inconsistent to have a built-in that differs in behavior from what a user could normally accomplish with a generator. |
18:21 | <Bakkot> | whatwg streams make use of them, I think |
18:22 | <Bakkot> | rbuckton why? generators are a convenient way of creating iterators, but not the only way |
18:22 | <rbuckton> | Of course, generators are essentially "lazy" since no code runs until the first call to `.next()`. |
18:22 | <michaelficarra> | jridgewell: it's iterator helpers, not iterable helpers |
18:23 | <jridgewell> | There's a constructor, no? |
18:23 | <jridgewell> | `new IterableHelpers(set)` |
18:23 | <littledan> | Bakkot: I thought that was pretty new/not universally shipped. But iterator .return/.throw has been shipped everywhere for years |
18:23 | <rbuckton> | michaelficarra: I still think targeting *iterator* and not *iterable* is the wrong abstraction. |
18:23 | <devsnek> | jridgewell: no |
18:23 | <ljharb> | rbuckton: every iterator-producing method in the spec already differs in that behavior. |
18:23 | <Bakkot> | littledan yeah, it's new. seems useful though! |
18:23 | <devsnek> | jridgewell: set.values().map() |
18:23 | <ljharb> | ES6 itself wasn't consistent between iterator-producing methods, and "what generators do" |
18:23 | <Bakkot> | re: "spec will be long", fwiw we could cut down the duplicate spec text the way we do for typed arrays and errors |
18:24 | <michaelficarra> | jridgewell: no, though there is Iterator.from |
18:24 | <devsnek> | Bakkot: i tried that, its still very long |
18:24 | <devsnek> | because each one has very different logic |
18:24 | <rbuckton> | I would have almost preferred `new Iterable(set.values()).map()`. |
18:24 | <jridgewell> | Does `Iterator.from` call `@@iterator`? |
18:24 | <devsnek> | yes |
18:25 | <michaelficarra> | yep |
18:25 | <jridgewell> | 👍 |
18:25 | <jridgewell> | Removed myself from the queue |
18:25 | <Bakkot> | devsnek: hm, that's surprising to me |
18:25 | <devsnek> | Yield macro just calls GeneratorYield or AsyncGeneratorYield |
18:25 | <devsnek> | no new machinery there |
18:26 | <Bakkot> | devsnek were you using abstract closures? |
18:26 | <devsnek> | Bakkot: i considered abstract closures |
18:26 | <devsnek> | i didn't come up with anything fantastic there |
18:26 | <Bakkot> | because with abstract closures you would just specify a State record and an abstract closure and .next would just invoke the abstract closure |
18:26 | <Bakkot> | it's only flatmap which would be more than that, I think |
18:27 | <devsnek> | i don't want to make something that needs special cases |
18:27 | <Bakkot> | meh |
18:27 | <michaelficarra> | I don't hate option 1: is there really a need to forward return/throw? |
18:27 | <devsnek> | just from a principled perspective |
18:27 | <rbuckton> | I wrote package that essentially has these helpers over iterables: |
18:27 | <rbuckton> | ```js |
18:27 | <rbuckton> | const { from } = require("iter-query"); |
18:27 | <rbuckton> | from(set.values()).map(...) |
18:27 | <rbuckton> | ``` |
18:27 | <rbuckton> | It makes a lot more sense to me at that level, though I understand the want to attach these to `%IteratorPrototype%` for convenience. |
18:27 | <Bakkot> | it's editorial |
18:27 | <devsnek> | i think we need good ways to add stdlib behaviour |
18:27 | <devsnek> | without special casing everything |
18:27 | <Bakkot> | I don't really think we should be optimizing for the spec being short |
18:27 | <Bakkot> | s/don't really think we should/strongly believe we should not/ |
18:27 | <shu> | agreed |
18:28 | <rbuckton> | I'm personally a fan of something like Option 1 (or at least, that uses `[@@iterator]`) |
18:28 | <devsnek> | it's not about being short as much as its about being consistent |
18:28 | <Bakkot> | as long as the user-observable behavior is consistent, I don't know why we would care that much |
18:28 | <devsnek> | option 3 is polyfillable btw |
18:29 | <Bakkot> | michaelficarra it seems important that `.throw` and `.return` forward to the underlying iterators, personally |
18:29 | <Bakkot> | like `iter().map(x => x)` should be as close as possible to `iter()` |
18:29 | <devsnek> | option 3 doesn't directly forward but it keeps the state consistent |
18:30 | <devsnek> | it will call the methods, i mean |
18:30 | <shu> | what is the difference between 2 and 3 other than editorial? |
18:30 | <devsnek> | in 2 they are plain objects that are iterators |
18:30 | <devsnek> | in 3 they are actual generators |
18:31 | <shu> | ah, but specced using spec machinery instead of JS code? |
18:31 | <devsnek> | yeah |
18:32 | <littledan> | personally I like option 3 the best, and agree with this goal of avoiding special-casing. |
18:33 | <littledan> | (just because we used one-off solutions each time in the past does'nt mean we need to keep doing that forever in the future...) |
18:33 | <michaelficarra> | I really don't think the presence of the methods should be conditional |
18:33 | <michaelficarra> | ljharb ^ |
18:33 | <littledan> | +1 |
18:34 | <robpalme> | is bradford on IRC? |
18:34 | <ljharb> | michaelficarra: i can understand that position for sure. but also APIs that consume iterators might be checking for the presence of those methods now, and taking simpler code paths when they're absent, so it might have an impact |
18:34 | <Bakkot> | agreed it might have an effect but I agree with michaelficarra anyway |
18:36 | <devsnek> | Bakkot: what do you think about async abstract operations |
18:36 | <devsnek> | abstract closures* |
18:37 | <Bakkot> | devsnek hmmm |
18:37 | <Bakkot> | no inherent problem with them I guess |
18:37 | <Bakkot> | would need to be defined |
18:37 | <ljharb> | from outside, it'd just be a regular one that returned a promise |
18:37 | <ljharb> | seems fine to me also |
18:37 | <shu> | no, more than that |
18:37 | <shu> | but that part of the spec could use our going over anyhow |
18:38 | <shu> | the part that actualy does the continuation wrapping in a promise |
18:38 | <devsnek> | it would definitely require spec work |
18:39 | <rbuckton> | I still wish the helpers went through `[@@iterator]` so they could be copied to other objects that are iterable. For all built-in iterators as well as generators, `[@@iterator]()` just returns `this`. Just depending on the presence of a `next` method makes me think of all of the issues with `then` we've had in the past. |
18:40 | <ljharb> | shu: i know it'd need lots more for "inside" the async abstract closure - i meant like from the callsites |
18:40 | <michaelficarra> | unfortunately I don't think we're going to come to a clear decision before this timebox is over :-( |
18:40 | <devsnek> | rbuckton: the main concern is preventing people from getting into patterns that rely on @@iterator being reusable when it isn't |
18:40 | <keith_miller> | ystartsev: Wait so would something like Symbol.generatorInitialize solve option 1? |
18:41 | <devsnek> | what is Symbol.generatorInitialize |
18:41 | <keith_miller> | that is called when the generator function is first called |
18:41 | <brad4d> | robpalme sorry got dropped |
18:41 | <devsnek> | to validate args and stuff? |
18:41 | <brad4d> | what were you asking me? |
18:41 | <ljharb> | keith_miller: how would i do that syntactically tho with regular generators |
18:41 | <keith_miller> | yeah |
18:41 | <ystartsev> | ping jorendorff for that |
18:41 | <ystartsev> | pinging* |
18:42 | <keith_miller> | You'd have to have something like (function* myGenerator() { ... })[Symbol.initializeGenerator] = function init() { validate(arguments[0]); } |
18:42 | <keith_miller> | not saying it's pretty |
18:43 | <keith_miller> | ljharb: but it would let you do stuff |
18:43 | <ystartsev> | oh no that wouldn't solve it actually |
18:43 | <ljharb> | at the risk of making all calling of generators slower? |
18:43 | <keith_miller> | Ah, ok |
18:43 | <ystartsev> | because you wont be able to forward properly |
18:43 | <Bakkot> | I think you just write a function which invokes and generator and returns it |
18:43 | <keith_miller> | ystartsev: the initialize could set up the forwarding |
18:44 | <Bakkot> | function f(){ validate(); return function*(){}(); } or whatever |
18:44 | <keith_miller> | ljharb: I mean initializing generators is already crazy slow |
18:44 | <keith_miller> | lol |
18:44 | <devsnek> | that's what option 3 does |
18:44 | <Bakkot> | right |
18:44 | <Bakkot> | and I think userland code would do the same thing if they want this |
18:44 | <devsnek> | yep |
18:44 | <keith_miller> | would do this? |
18:44 | <keith_miller> | Or would make their equivalent of the polyfill? |
18:44 | <devsnek> | well actually what i want is a way to create a generator that has no initial yield |
18:45 | <keith_miller> | I mean sure, this is just a hacky way to get that |
18:45 | <jridgewell> | I don't think complicating generator functions even more is a good solution to this |
18:45 | <devsnek> | oh no i don't think we should do that as part of this |
18:45 | <devsnek> | none of these options ask for that |
18:46 | <keith_miller> | I mean option 3 is effectively asking VMs to do it |
18:46 | <jridgewell> | Options 2 and 3 seem to be regular functions that could return generator instances, which seems a better solution |
18:46 | <keith_miller> | so... for me there's no difference but I'm an implementor |
18:47 | <jridgewell> | Giving more magic to generator functions makes the transform muchhh more complicated |
18:47 | <devsnek> | did we get consensus to use option 3 there? |
18:47 | <jridgewell> | I already don't want to maintain the regenerator transform |
18:48 | <ystartsev> | we didn't really get a conclusion here right? |
18:48 | <devsnek> | yeah that was weird |
18:48 | <michaelficarra> | wow how did we get this far down on the agenda? |
18:48 | <michaelficarra> | time constraints? |
18:48 | <devsnek> | afaik the point of that presentation was to get consensus on how we should proceed |
18:48 | <haxjs> | I'm not sure how option 3 forward the arg of first next call (which function.sent need)? |
18:49 | <devsnek> | haxjs: StartIteratorHelper calls .next() once |
18:49 | <devsnek> | which is why %SyncMap% starts with a Yield() |
18:51 | <haxjs> | Can't find it in https://gist.github.com/jorendorff/35504c2553170be98fc2810ccf60c608 🤔 |
18:51 | <devsnek> | haxjs: the value is lost there |
18:51 | <devsnek> | that's not option 3 |
18:51 | <jridgewell> | That's Option 1, I think |
18:52 | <haxjs> | Iterator.prototype = { |
18:52 | <haxjs> | map(mapper) { |
18:52 | <haxjs> | let it = SyncMap(this, mapper); |
18:52 | <haxjs> | it.next(); |
18:52 | <haxjs> | return it; |
18:52 | <haxjs> | } |
18:52 | <haxjs> | }; |
18:52 | <devsnek> | haxjs: more or less, yeah |
18:56 | <ystartsev> | re: iterators |
18:56 | <ystartsev> | i have opened this issue: https://github.com/tc39/proposal-iterator-helpers/issues/97 |
18:56 | <ljharb> | keith_miller: re your queue item; they already can't, because they're inside an expression position |
18:56 | <keith_miller> | ljharb: ?? |
18:57 | <keith_miller> | I'm saying it's the same result as an eval |
18:57 | <ystartsev> | do you all think we could revisit this for 5 min on thursday? |
18:57 | <ljharb> | keith_miller: ah, k |
18:59 | <ljharb> | brad4d: an IIFE wouldn't necessarily preserve `super`, `arguments`, `this`; nor `await`, and it would make control flow questions complicated |
19:01 | <brad4d> | an arrow iife preserves `super`, `this`, `arguments` |
19:01 | <brad4d> | is it really desirable to return / break from a do {} expresssion? |
19:01 | <devsnek> | yes |
19:01 | <ljharb> | imo no, but some folks think yes |
19:01 | <jridgewell> | Arrow won't preserve `yield` |
19:01 | <ljharb> | or `await` |
19:02 | <devsnek> | i really want control flow from do expressions |
19:02 | <jridgewell> | You can use `await async () => { await x }` |
19:02 | <shu> | bradleymeck: control flow non-local to the do block is contentious |
19:02 | <jridgewell> | But not for `yield`, because we still don't have arrow generators |
19:04 | <bradleymeck> | shu: did you mean to ping me on that? |
19:05 | <shu> | bradleymeck: nope, sorry :) |
19:05 | <shu> | brad4d: ^ |
19:05 | <brad4d> | shu ack |
19:07 | <bradleymeck> | i think control flow has utility in various positions that are annoying to deal with otherwise but not fatal to be missing |
19:10 | <bradleymeck> | there are some really odd things you can do though, like `do {continue}` on a destructuring default assignment to skip a loop |
19:12 | <ljharb> | does return/break/continue inside eval already work in an expression position? |
19:12 | <devsnek> | no |
19:12 | <brad4d> | you mean `let {x = do {continue}} = obj;` would continue a loop if `obj` doesn't have an `x`? |
19:12 | <brad4d> | I think that is harmful to readability. |
19:13 | <devsnek> | to me that's a linting concern |
19:13 | <devsnek> | there are places (outside of destructuring declarations) where continue can be useful |
19:14 | <akirose> | that's how zkat initially wrote those proposals iirc—pattern matching + do expressions intended to move through committee side-by-side |
19:14 | <brad4d> | devsnek could you point me at a use-case where flow-control inside a `do{}` would be beneficial? I can't seem to come up with one on my own? |
19:14 | <leobalter> | ljharb: the coupling with pattern matching would be a nice thing to discuss in incubator(ish?) calls |
19:15 | <leobalter> | idk Mark's irc handler if any |
19:15 | <ljharb> | leobalter: agreed; we're still pretty far from being ready for that tho (mpcsh) |
19:15 | <rkirsling> | it's been discussed in the pattern matching calls |
19:15 | <devsnek> | brad4d: there was one in the slides |
19:15 | <jridgewell> | Lol |
19:16 | jridgewell | sees myself to TDZ |
19:16 | <mpcsh> | ljharb leobalter Bakkot: should we try to get both of these proposals on an incubator call? |
19:16 | <ljharb> | leobalter: happy to invite you to the next pattern matching call if you're interested |
19:16 | <leobalter> | yes, please |
19:16 | <ljharb> | mpcsh: yes, but i think after i've got the PR ready to update the proposal :-) |
19:17 | <mpcsh> | 👍 |
19:18 | <rbuckton> | I've seen a fair amount of Kotlin code that does continue/break/return in expression positions: |
19:18 | <rbuckton> | ```kotlin |
19:18 | <rbuckton> | for (x in list) { |
19:18 | <rbuckton> | val y = x?.y ?: continue; |
19:18 | <rbuckton> | ... |
19:18 | <rbuckton> | } |
19:18 | <rbuckton> | ``` |
19:19 | <jridgewell> | There's a goog internal server language that has `or exit` all over the place. |
19:19 | <littledan> | I share Bakkot 's intuition about blocking return, continue and break from expressions breaking some kind of invariant that we have |
19:19 | <rkirsling> | rbuckton: I don't think it'd be contentious without the visible boundary of `do {}` |
19:19 | <littledan> | so, I guess this is what exceptions are for? |
19:19 | <ljharb> | same; flow control doesn't belong in expression position |
19:20 | <rbuckton> | rkirsling: which wouldn't be contentious? `x?.y ?? continue` or `x?.y ?? do { continue; }`? |
19:20 | <devsnek> | there is no such invariant in how we have specified the language |
19:20 | <devsnek> | in fact completion values help allow it |
19:20 | <ljharb> | devsnek: sure, i didn't say they can't be. i said they shouldn't be. |
19:21 | <devsnek> | dan said there was an invariant |
19:21 | <devsnek> | which i strongly disagree on |
19:21 | <ljharb> | oh |
19:21 | <ljharb> | well, the invariant is that you currently can't do any non-throw flow control in expression position |
19:21 | <rkirsling> | rbuckton: the former wouldn't be, is my argument. "everything is an expression" doesn't induce confusion where "crossing the border between worlds" does |
19:21 | <ljharb> | it may or may not be intentional, but it remains an invariant |
19:21 | <leobalter> | I'm totally +1 to do expressions. Although, a spec draft would make it easier for an overview |
19:22 | <littledan> | hmm, I'm not sure why you'd have to care so much about the microtask ticks... I'd hope not many developers have to think in those terms |
19:22 | <ljharb> | people often think about overhead |
19:22 | <littledan> | devsnek: Well, it's currently an invariant; we can disagree about the priority of preserving it |
19:22 | <littledan> | like, it's currently a fact about JS that that doesn't happen |
19:23 | <devsnek> | that's not an invariant its just a hole |
19:23 | <rbuckton> | rkirsling: I have an issue on the do-expressions repo that suggests dropping `do {}` and updating ParenthesizedExpression to allow most statements. Then you would have `x ?? (continue)`. The parens are necessary to preserve precedence of `,`, which was one of Bakkot's concerns about throw expressions. |
19:23 | <littledan> | the JS spec notation allows all kinds of stuff that we have decided we won't do |
19:23 | <devsnek> | that's like saying %Iterator%.prototype.map not existing is an invariant of the language |
19:23 | <ljharb> | and indeed, it is right now |
19:23 | <littledan> | so, in general, I think that assertions of things being "invariants" are value judgements, and I'm comfortable calling my claim a value judgement |
19:23 | <ljharb> | devsnek: but i think there's a categorical difference there that you surely are aware of |
19:23 | <littledan> | it's a statement about what we want in the future |
19:24 | <ljharb> | drousso: what about the dev tools/repl? |
19:24 | <devsnek> | value judgement seems like a better term |
19:24 | <drousso> | ljharb i don't think those people think of that as "completion" |
19:24 | <rkirsling> | I'm confused by the non-specific confusion |
19:24 | <ljharb> | drousso: and yet that's what it is |
19:24 | <ljharb> | drousso: so would it be more palatable if do expressions were explained as "like in the dev tools"? |
19:25 | <rkirsling> | the goal is to continue preserving "ignorance is bliss" around completion values, was my understanding |
19:25 | <ljharb> | drousso: since that's something virtually every dev already understands? |
19:25 | <drousso> | ljharb im not convinced that developers even know what's going on in the console |
19:25 | <ljharb> | drousso: they figure it out very quickly. they type `3` and the result is `3` |
19:25 | <ljharb> | or `if (true) { 3 }` and the result is `3` |
19:25 | <drousso> | it doesn't work exactly like the devtools tho |
19:25 | <devsnek> | i just don't get like |
19:26 | <ljharb> | drousso: aside from "devtools exposes internals" and whatnot, how does it differ? |
19:26 | <devsnek> | why can't we just say some people don't like control flow there, and leave it to linters |
19:26 | <drousso> | oh wait i was thinking of something different |
19:26 | <drousso> | yes it does work the same as devtools |
19:26 | <drousso> | i don't think that that's a good way of explaining it though |
19:27 | <drousso> | as that doesn't clarify anything about what's happening |
19:27 | <drousso> | it just provides a "if you want to see it in action, use devtools" |
19:27 | <drousso> | it doesn't explain what's actually happening |
19:27 | <drousso> | s/it/devtools |
19:27 | <ljharb> | drousso: the explanation isn't needed tho, if everyone already intuits how it's supposed to work |
19:27 | <robpalme> | Younies is unavailable for his presentation so Record & Tuple is next. |
19:27 | <drousso> | i disagree with that |
19:27 | <drousso> | vehemently |
19:28 | <rkirsling> | can you give specific examples of what the people you spoke to thought the basic cases should do instead? |
19:28 | <drousso> | the function declaration |
19:28 | <haxjs> | +1 for banning too :) |
19:29 | <drousso> | many i spoke to thought that that would result in the function declaration being used |
19:29 | <littledan> | I disagree with the notion that a proposal like this would need to add new capabilities (like return/break/continue from an expression) to carry their own weight; I'm not sure if waldemar was alluding to that |
19:29 | <devsnek> | waldemar said this proposal isn't worth it without control flow |
19:29 | <Bakkot> | drousso you get a syntax error; I think "what does this code do? oh it's a syntax error" is not that big of a deal |
19:29 | <haxjs> | I think many devs only want some sugar for IIFE. |
19:29 | <devsnek> | i certainly won't use it as much as i was planning to without control flow |
19:30 | <ljharb> | haxjs: why does that mean they shouldn't get it? |
19:30 | <drousso> | i don't think syntax errors are a good way of teaching a developer how to use something |
19:30 | <drousso> | in fact i've spoken with many developers who find JS errors often unhelpful |
19:30 | <devsnek> | that's more on engines |
19:30 | <ljharb> | unfortunately we don't specify error messages, that's on the browsers |
19:30 | <ljharb> | * browsers/engines |
19:31 | <devsnek> | engines don't even bother to implement parsers which support reporting multiple errors |
19:31 | <ystartsev> | drousso: we had pretty strong support internally for this, its interesting that your front end developers have such a strong issue with this. Are there certain patterns that they are using that makes it more difficult? |
19:31 | <haxjs> | ljharb so any other capabilities just add confusion even they are powerful :) |
19:31 | <ystartsev> | and also, yes -- engines cannot really improve their error messages due to web compat |
19:31 | <ljharb> | haxjs: i agree that return/continue/break in do expressions are powerful and also add confusion, yes |
19:31 | <drousso> | that is true |
19:31 | <ystartsev> | we tried and had to back it out |
19:31 | <drousso> | im not saying that's the fault of this situation |
19:31 | <drousso> | or that it's up to this proposal to fix it |
19:32 | <drousso> | just that i believe errors are not a good way to teach things |
19:32 | <drousso> | brad4d +1000 |
19:32 | <ljharb> | drousso: i think it's very unlikely devs will even try to do these things in the first place tho |
19:32 | <drousso> | brad4d completely agree |
19:32 | <ljharb> | drousso: iow, i think that most devs won't ever run into it. |
19:32 | <drousso> | ljharb if that's the case, why add it in the first place? |
19:33 | <rkirsling> | I will say that I too originally expected that `return` would serve as an early out _for the do expr_ |
19:33 | <drousso> | or wait do you mean these "edge cases" or `do` in general? |
19:33 | <ljharb> | drousso: because they *will* try to use all the non-error cases in a block in expression position |
19:33 | <ljharb> | drousso: i mean the edge cases |
19:33 | <ljharb> | drousso: tons of people will immediately try to use the non-edge-cases in a ton of code |
19:33 | <rkirsling> | (but obviously it's moot if it's banned) |
19:33 | <ljharb> | drousso: especially in the react community inside jsx, not that i think that needs to be a motivation |
19:33 | <ljharb> | rkirsling: well, i'm saying i don't think they'll even run into the bans |
19:34 | <ljharb> | rkirsling: i think most users won't ever discover those are missing because they'll never think to try it |
19:34 | <haxjs> | But, I also think banning everything controversial not really solve the problems, for example how can we break a loop and return a value (it seems it will be banned as slide) |
19:34 | <ljharb> | haxjs: same way you do now: return a sentinel value, and then check it in the main loop body |
19:35 | <rbuckton> | Back when I presented throw expressions I was asked by many in the committee to investigate converting other statements to expressions, especially `return`, `continue`, `break`. I'm actually surprised at the number of people in favor of banning those statements in a `do {}` expression. |
19:35 | <ystartsev> | re: iterator helpers issue: https://github.com/tc39/proposal-iterator-helpers/issues/97 |
19:35 | <ystartsev> | please comment so we can get to a solution! |
19:35 | <devsnek> | ljharb: no right now you just `return` |
19:35 | <haxjs> | ljharb: so i guess dev would expect iife which can return a value directly :-P |
19:35 | <devsnek> | if anything not allowing control flow is a refactoring hazard |
19:35 | <ljharb> | devsnek: not if the value comes from an IIFE, or another function |
19:36 | <ljharb> | haxjs: they'll expect their do expression body to be able to produce a value; i'm saying they won't expect it to be able to force the containing function to return |
19:37 | <brad4d> | @devs |
19:37 | <brad4d> | devsnek allowing flow of control is a refactoring hazard if you're inlining a function |
19:39 | <brad4d> | `do{}` expressions would make function inlining much easier if they really act like inline functions |
19:39 | <haxjs> | ljharb: what I'm talking is code like `do { while(...) { if (...) { 1 ; break } } }` |
19:40 | <ljharb> | haxjs: that would work fine, because the loop you're breaking is fully inside the `do` |
19:40 | <ljharb> | haxjs: i think |
19:40 | <ljharb> | haxjs: ohh right but it wouldn't because of ending in a loop |
19:40 | <ljharb> | haxjs: so then `do { let v; while (…) { if (…) { v = 1; break; } } v; }` |
19:41 | <haxjs> | yeah, this is why i say devs may like iife sugar... |
19:42 | <ljharb> | gotcha, agreed |
19:43 | <ljharb> | well, i see what you mean anyways. i don't think they actually want a function invocation |
19:43 | <haxjs> | As i understand, basically current minimal proposal just have very similar power or even less power of iife. |
19:43 | <ljharb> | and less overhead |
19:44 | <haxjs> | yeah, so maybe iife sugar is what we need? |
19:44 | <Bakkot> | current proposal lets you do something you can't with IIFE, which is await/yield |
19:45 | <Bakkot> | await you can do with a microtask tick, yield... you just can't do, I think, at least without a bunch of wrappers |
19:45 | <ljharb> | ^ that |
19:45 | <haxjs> | yeah! so it's iife + await async iife sugar :-P |
19:46 | <Bakkot> | bterlson/other chairs: voice jorendorff ? |
19:47 | <sffc> | I'll figure out my mic |
19:47 | <sffc> | go to the next speaker |
19:49 | <sffc> | I'll follow up on GitHub and/or in the hallway track |
19:50 | <ystartsev> | Bakkot: on it |
19:52 | <jorendorff> | thank you |
19:55 | <Bakkot> | complex numbers don't have -0, bah |
19:56 | <jorendorff> | haha |
19:57 | <Bakkot> | during the editor update I misspoke: it's actually caiolima doing the work to fix the number types in the spec. mea culpa. |
19:58 | <leobalter> | time is over for today, but we have someone logged in as Guest in the call, I believe we should avoid that tomorrow. |
19:58 | <rkirsling> | indeed |
20:03 | <leobalter> | SameValue makes sense if we want to follow pattern with `includes`, right? |
20:04 | <jorendorff> | well ... it seems more directly applicable to look at what `===` does, than `includes` |
20:04 | <Bakkot> | leobalter SameValueZero, technically |
20:05 | <Bakkot> | `[-0].includes(0)` and `[NaN].includes(NaN)` are both true |
20:06 | <mpcsh> | I am opposed to shortening lunch, for the record. the hallway-track social time is nice |
20:08 | <rkirsling> | mpcsh: I don't think we necessarily need to decrease break time, perhaps just rearrange it |
20:08 | <sffc> | Ujjwal and I are in the hallway track; would be nice to have more |
20:09 | <haxjs> | I feel we should keep consistency with normal `===`. The weirdness of `===` is coming from IEEE float, developers are inevitable to learn it, add an exception rule for tuple/record just make things worse and never really solve anything. |
20:09 | <sffc> | I have some thoughts about Record/Tuple that I'd like to discuss with the champions |
20:10 | <akirose> | did we fall back to hubs? |
20:10 | <ystartsev> | lets use online town for today |
20:10 | <akirose> | kk |
20:10 | <ystartsev> | and tomorrow switch to hubs |
20:10 | <ystartsev> | online town has some issues with distance that hubs didnt |
20:10 | <bradleymeck> | i think having any value fail to ever be === if a sub component is NaN is... highly surprising |
20:12 | <rickbutton> | sffc: do you want to chat now? I'm free, or via email? |
20:13 | <sffc> | I'm in the town, plus 5 others now |
20:13 | <rickbutton> | oh right |
20:13 | <rickbutton> | will be there shortly |
20:14 | <bterlson> | Chairs discussed shorter lunch break and we will stick with 1 hour for now. Delegates with free time should consider joining us in the hallway track! /cc Bakkot |
20:14 | <leobalter> | akirose bterlson how hard would it be to change my initials in the notes? I'd rather go with LFB (to reflect my actual initials), but I don't wanna cause too much work |
20:14 | <mpcsh> | leobalter: I switched mine from MCN to MPC a little while ago, it's not bad |
20:15 | <mpcsh> | just do a find & replace for today's notes, and submit a PR for old notes |
20:19 | <TabAtkins> | Bakkot: How are complex numbers avoiding -0? |
20:25 | <Bakkot> | TabAtkins the traditional definition of complex numbers does not include two distinct 0s |
20:26 | <Bakkot> | (or four, I guess) |
20:26 | <TabAtkins> | Well sure, but the traditional definition of the reals doesn't either. |
20:26 | <TabAtkins> | Complex-in-JS would probably have some -0s in it |
20:27 | <TabAtkins> | (Probably... 3? If they're just a pair of Numbers.) |
20:28 | <Bakkot> | 0,0, -0,0, 0,-0, -0,-0 is four |
20:28 | <Bakkot> | I guess just three -0s though |
20:28 | <TabAtkins> | 0,0 is a positive zero, yeah ^_^ |
20:32 | <ljharb> | jorendorff: hopping into the hallway track if you wanted to chat now |
20:33 | <jorendorff> | @Bakkot believe it or not I think Brendan Eich exposed -0 to JS based on advice from Guy Steele that had to do with complex numbers |
20:33 | <Bakkot> | does css have a -0 which is observably different from 0? |
20:33 | <Bakkot> | jorendorff fascinating |
20:33 | <jorendorff> | in my hearing he only ever explained it far too fast for me to follow |
20:34 | jorendorff | looks for the hallway-track link |
20:34 | <Bakkot> | it's in the reflector I think |
20:34 | <shu> | "far too fast": checks out for brendan! |
20:37 | <jorendorff> | got it |
20:39 | <TabAtkins> | Bakkot: CSS grew a -0 that is *slightly* observable, to preserve IEEE semantics and JS compatibility. It gets censored into a plain zero the moment is would escape a math function. |
20:39 | <TabAtkins> | But yeah, `margin-left: calc(1/0);` is different from `calc(1/-0)`. |
20:43 | <Bakkot> | good times |
20:43 | <Bakkot> | -0 was a terrible idea |
20:43 | <Bakkot> | I will die mad about that |
20:44 | <jorendorff> | can't wait for some brave new language to just say no |
20:46 | <shu> | what... do i do for the rest of my day |
20:46 | <ljharb> | Bakkot: but how else can i express the limit of an integral approaching zero from the negative direction |
20:46 | <TabAtkins> | Oh jeez I forgot it was 3pm Central, I was waiting for the meetig to start again |
20:46 | <TabAtkins> | ljharb: OH easy, don't |
20:47 | <ljharb> | my calculus teacher would be horrified |
20:47 | <Bakkot> | ljharb: 0 |
20:47 | <rickbutton> | shu: sleep |
20:47 | <Bakkot> | how do you express the limit of an integral approaching 1 from the negative direction? :P |
20:48 | <ljharb> | touché |
20:49 | <jorendorff> | ljharb: suppose we go with option 2, to me the most obvious way to do that would be different iterator types for map, filter, take, drop, etc. |
20:49 | <jorendorff> | ljharb: so, different internal slots for each |
20:49 | <jorendorff> | and every method has a brand check |
20:50 | <rkirsling> | Bakkot: +1 re -0 |
20:51 | <rkirsling> | so many NaNs and yet two zeroes is too many |
20:51 | <rkirsling> | *two 0s |
20:52 | <ljharb> | jorendorff: sure, seems right to me |
20:52 | <ljharb> | jorendorff: altho it also seems doable as a single object type |
20:52 | <jorendorff> | ljharb: ok, right, I'm thinking about that now |
20:53 | <ljharb> | it might get weird with some of the types tho |
20:53 | <jorendorff> | they do all have something in common, which is that they all have a single source iterator... except there's `Iterator.prototype.flatMap()`, the sole exception |
20:53 | <ljharb> | that doesn't have a source iterator? or it has multiple |
20:53 | <ljharb> | (those were questions, not statements) |
20:54 | <Bakkot> | I think you'd have an abstract op which returned a list of values to yield, and everything except flatmap would return a list of length exactly 1 |
20:54 | <Bakkot> | boom, no more special case |
20:55 | <jorendorff> | ok, so if we do this, we can have a common implementation of throw and return shared by all the iterator helpers |
20:55 | <jorendorff> | the common thing needs a name |
20:55 | <ljharb> | and the common thing would just be "innerIterator?.return()` etc? |
20:55 | <Bakkot> | if they're different objects, the common thing would be spec-internal, right? |
20:55 | <jorendorff> | Bakkot: wait, actually I didn't follow that |
20:55 | <Bakkot> | so the name doesn't matter so much |
20:55 | <ljharb> | or sorry `innerIterator.return?.()` |
20:55 | <Bakkot> | IteratorHelperReturn or whatever |
20:56 | <jorendorff> | Bakkot: "abstract op which returned a list of values to yield" I don't understand when this abstract op is used |
20:56 | <jorendorff> | I... don't think I understand this scheme at all actually |
20:57 | <Bakkot> | jorendorff the idea is that each helper would define such an abstract op, and the `next` would know how to invoke the abstract op |
20:57 | <Bakkot> | we can talk about it more tomorrow |
20:57 | <Bakkot> | hard to talk about it without code |
20:57 | <jorendorff> | ok, and the helper would have abstract ops for `throw` and `return` too? |
20:58 | <Bakkot> | the helper would specifically be the implementation of `.next` |
20:58 | <Bakkot> | but yeah maybe? |
20:58 | <Bakkot> | not sure |
20:58 | <Bakkot> | have to write it out |
20:59 | <jorendorff> | ok, let's meet up tomorrow and talk about it more, maybe sketch some things out? |
20:59 | <Bakkot> | y |
20:59 | <jorendorff> | ljharb: are you free tomorrow afternoon at 2PM Pacific time to chat? |
20:59 | <devsnek> | return/throw may also have to touch logic in the helper itself |
20:59 | <devsnek> | which is why i mentioned that it isn't ideal to have them just be methods that call the inner iterator |
21:00 | <ljharb> | jorendorff: yep, works for me |
21:00 | <Bakkot> | devsnek what logic in the helper do they need to touch? |
21:00 | <devsnek> | idk |
21:00 | <Bakkot> | other than "mark as stale", I guess |
21:00 | <devsnek> | flatMap is one |
21:00 | <jorendorff> | ljharb: thanks very much, let's all meet here 2PM Pacific tomorrow, then we'll set up a video chat |
21:01 | <devsnek> | there could be more in the future |
21:01 | <jorendorff> | I think flatMap is the only one; the others all do the same thing for throw (forward it) and return (forward it0 |
21:01 | <jorendorff> | right, the only one at present |
21:01 | <Bakkot> | what does flatmap need to do specially? |
21:01 | <devsnek> | close two iterators |
21:01 | <Bakkot> | ah, yeah, sure |
21:01 | <devsnek> | we could have more in the future |
21:01 | <jorendorff> | flatmap is `for (let iterable in this) for (let value in iterable) yield value;` |
21:02 | <devsnek> | i'd rather do the work now to have a proper way to specify generators in our stdlib |
21:02 | <devsnek> | than keep having to make weird iterator things forever |
21:02 | <Bakkot> | I kind of prefer making weird iterator things forever, even if we had a proper way to specify generators |
21:02 | <jorendorff> | If the goal is to produce a common type of iterator that shares some implementation details with other iterators, with common throw/return methods, |
21:02 | <Bakkot> | ease of spec isn't really a priority |
21:03 | <devsnek> | i want consistency |
21:03 | <devsnek> | of how our systems behave |
21:03 | <jorendorff> | that doesn't expose implementation details, |
21:03 | <Bakkot> | weird iterator things forever is the consistent thing |
21:03 | <jorendorff> | ...then the exact thing we are talking about as an ideal end state is... a generator |
21:03 | <devsnek> | ^ |
21:03 | <Bakkot> | right, if that's the goal |
21:03 | <jorendorff> | I agree weird iterator things forever is consistent with existing practice |
21:03 | <Bakkot> | I don't really share that goal |
21:03 | <Bakkot> | I don't object to it, but don't see why it's valuable |
21:04 | <jorendorff> | ease of spec is not what I'm concerned with |
21:04 | <devsnek> | i don't want generators for the sake of generators, i want them because they handle all the reentrancy and cleanup and whatnot properly |
21:04 | <jorendorff> | ease of using and understanding the spec matters to me |
21:05 | <devsnek> | i'm more concerned with ease of understanding but i also have to write all these methods so... |
21:05 | <jorendorff> | anyway -- we should talk more tomorrow. thanks again for your time |
21:06 | <jorendorff> | Bakkot: oh, btw - you mentioned today that you don't think programmers will try to use break/return from within do-expressions |
21:07 | <devsnek> | i feel like half of all js code is early returns |
21:07 | <jorendorff> | Bakkot: i think they will :( because there are lots of expression languages, most notably Ruby and Rust but I think it's not even uncommon these days |
21:08 | <Bakkot> | jorendorff did not mean to imply that |
21:08 | <jorendorff> | expression languages that have weird control flow stuff like `return` |
21:08 | <jorendorff> | (Smalltalk too) |
21:08 | <Bakkot> | I think people won't try to end their do-exprs with declarations |
21:08 | <Bakkot> | and yeah I've used the feature in ruby |
21:08 | <Bakkot> | and rust |
21:08 | <jorendorff> | oh, that I agree we can just call it a SyntaxError |
21:09 | <jorendorff> | my comment is limited to break/continue/return |
21:09 | <Bakkot> | yeah, like I say I didn't mean to imply people won't try that |
21:09 | <Bakkot> | if I did say that i misspoke |
21:09 | <jorendorff> | i probably misheared, i don't mean to put words in your mouth! |
21:10 | <rbuckton> | The TypeScript source heavily utilizes function declarations that trail the source code that uses them. I could see function declarations at the end of a `do {}` tripping people up if they're refactoring. |
21:10 | <Bakkot> | sounds like a good reason for that to be a syntax error! |
21:10 | <Bakkot> | if some people are actually going to expect to get the before-declaration value |
21:10 | <rbuckton> | No, that still would trip them up. |
21:10 | <Bakkot> | instead of the function itself |
21:11 | <rbuckton> | it would be better if function declarations don't contribute to the resulting value |
21:11 | <Bakkot> | People getting tripped up on things being a syntax error is the least bad kind of people getting tripped up. |
21:11 | <Bakkot> | Strong disagree. |
21:11 | <jorendorff> | agree with "least bad" |
21:11 | <Bakkot> | if `do { 1; function f(){} }` results in `1` and not a function, that's... not... correct |
21:11 | <rbuckton> | why do you disagree? |
21:11 | <ljharb> | yeah that'd be more bad |
21:11 | <sffc> | @chairs - can you inform the delegates of the decision of lunch break duration before tomorrow, so that I can adjust my schedule accordingly? I'm trying to fit in non-TC39 meeting during the lunch break. |
21:12 | <rkirsling> | definitely more bad |
21:12 | <rbuckton> | To me that argues for explicit syntax for the return value. |
21:12 | <Bakkot> | sffc bterlson said above they will stick with 1 hour |
21:12 | <sffc> | I have a slight preference for 60-minute break because of that reason (ability to schedule a meeting during prime time) |
21:12 | <Bakkot> | rbuckton how so? |
21:12 | <Bakkot> | You can get the return value you want by putting an expression as the last statement. |
21:12 | <bterlson> | sffc: confirm, 1 hr lunch will continue |
21:13 | <Bakkot> | and putting a declaration as the last statement gives a syntax error |
21:13 | <sffc> | Sounds good, thanks |
21:13 | <Bakkot> | so you can do the things you want, and the surprising cases you can't run into |
21:13 | <jorendorff> | Bakkot: separate weird comment: `yield` expressions can already return ... you just ... don't have any control over it |
21:13 | <rbuckton> | Having to move the function is a mess WRT refactoring tooling |
21:13 | <ljharb> | or at least, you'll run into them immediately at dev time |
21:14 | <Bakkot> | jorendorff yeah I was avoiding bringing that up |
21:14 | <jorendorff> | I'm not sure what to conclude from that |
21:14 | <Bakkot> | it is true but obscure and also very strange |
21:14 | <jorendorff> | probably for the best |
21:14 | <devsnek> | i would conclude that making rules about what can occur in do expressions is dangerous |
21:14 | <jorendorff> | i mean, it means that the spec-internal wiring |
21:14 | <Bakkot> | rbuckton but at least you get an error instead of the wrong thing! |
21:14 | <Bakkot> | so you know your refactor needs to change |
21:14 | <rbuckton> | Consider this code: |
21:14 | <rbuckton> | ```js |
21:14 | <rbuckton> | const fn = (x) => { |
21:14 | <rbuckton> | // 1 |
21:14 | <rbuckton> | return x.map(visit); |
21:14 | <rbuckton> | // 2 |
21:14 | <rbuckton> | function visit(y) { ... } |
21:14 | <rbuckton> | }; |
21:14 | <rbuckton> | const a = fn(b); |
21:14 | <Bakkot> | seems fine to me |
21:14 | <rbuckton> | ``` |
21:14 | <rbuckton> | If I use tooling to inline `fn` as a `do` expression, the tooling would have to move `visit`, but does it also move `// 2` with it? |
21:14 | <jorendorff> | for returning from an expression, already exists and works |
21:15 | <Bakkot> | rbuckton probably yes; most tooling considers comments to attach to the following statement, which is decent heuristic |
21:15 | <Bakkot> | rbuckton but also I don't think "tooling which is automatically rewriting code will need to make a call about how to handle comments" is an argument which should have much weight |
21:16 | <rbuckton> | ``` |
21:16 | <rbuckton> | const fn = (x) => { |
21:16 | <rbuckton> | // 1 |
21:16 | <rbuckton> | return x.map(visit); |
21:16 | <rbuckton> | // long explanation of what visit does |
21:16 | <rbuckton> | // even more text... |
21:16 | <rbuckton> | function visit(y) { ... }; |
21:16 | <rbuckton> | }; |
21:16 | <rbuckton> | ``` |
21:16 | <ljharb> | comment attachment is definitely already an intractable problem imo |
21:16 | <Bakkot> | tooling usually considers all of the comments between statement A and statement B to be attached to B, unless they occur at the end of line A, in my experience |
21:16 | <ljharb> | rbuckton: if `visit` requires that much explanation, then why isn't it extracted into a separate tested module? |
21:16 | <Bakkot> | so the long explanation would get moved |
21:16 | <rbuckton> | I disagree, tooling is an important consideration, which is why there's a monthly tooling call |
21:17 | <Bakkot> | I agree that tooling is important. |
21:17 | <devsnek> | how about if do expressions allows control flow i'll write the eslint rule to disallow it for you |
21:17 | <Bakkot> | I disagree with the specific claim that "tooling which is automatically rewriting code will need to make a call about how to handle comments" is an argument which should have much weight |
21:17 | <Bakkot> | that is more specific than "tooling". |
21:18 | <ljharb> | devsnek: first you'd have to convince eslint core to add the rule, under their new rules policy |
21:19 | <devsnek> | what's that policy |
21:19 | <Bakkot> | devsnek I will continue having to read other code bases |
21:19 | <Bakkot> | more to the point, so will everyone else |
21:19 | <devsnek> | and they will read my beautiful code that uses control flow |
21:19 | <devsnek> | and say "wow bakkot's code is lacking in this amazing pattern" |
21:19 | <devsnek> | :P |
21:19 | <Bakkot> | they will read your code that uses control flow |
21:19 | <Bakkot> | that part I agree with |
21:20 | <Bakkot> | devsnek the policy is that they do not accept rules which just ban syntax, if you could write it with the forbidden syntax rule |
21:20 | <rbuckton> | Comparison: |
21:20 | <rbuckton> | ``` |
21:20 | <rbuckton> | // no cf |
21:20 | <rbuckton> | for (const x of ar) { |
21:20 | <rbuckton> | const y = x.y; |
21:20 | <rbuckton> | if (y === null || y === undefined) continue; |
21:20 | <rbuckton> | ... |
21:20 | <rbuckton> | } |
21:20 | <rbuckton> | // with cf |
21:20 | <rbuckton> | for (const x of ar) { |
21:20 | <rbuckton> | } |
21:21 | <rbuckton> | ``` |
21:21 | <rbuckton> | // with cf |
21:21 | <rbuckton> | for (const x of ar) { |
21:21 | <rbuckton> | const y = x.y ?? do { continue; } |
21:21 | <rbuckton> | } |
21:21 | <rbuckton> | ``` |
21:21 | <Bakkot> | rbuckton that example does not make me want to allow control flow |
21:21 | <rbuckton> | oi (code fragments in irccloud can be a pain) |
21:21 | <shu> | why is that better? |
21:21 | <shu> | rbuckton: yeah i may be having the opposite reaction you want |
21:21 | <shu> | rbuckton: which is that with cf is less readable |
21:21 | <shu> | i agree that it's terser |
21:21 | <ljharb> | i agree with that ^ |
21:22 | <shu> | and the === null || === undefined is contrived |
21:22 | <shu> | if the intention is to test for nullish, == undefined is fine |
21:22 | <rbuckton> | shu: Its concise, same reason we have `??` now instead of `x !== null || x !== undefined ? x : y` |
21:22 | <rbuckton> | `== undefined` is not fine because of `document.all` |
21:22 | <shu> | rbuckton: that concision did not cross statement/expression boundaries |
21:22 | <shu> | rbuckton: fair enough for document.all |
21:23 | <Bakkot> | the point about ?? is that it is consise _and clearly expresses intent_ |
21:23 | <Bakkot> | having a `const y =` which is also sometimes a `continue` does not, to me, clearly express intent |
21:23 | <jorendorff> | no but motivating examples should not be this ... extremely specific |
21:23 | <rbuckton> | It seems strange to be able to do `x ?? do { throw ... }` but not `x ?? do { continue; }` |
21:23 | <Bakkot> | why? |
21:23 | <Bakkot> | expressions can throw, that's normal |
21:23 | <Bakkot> | expression can't continue |
21:23 | <Bakkot> | exceptions are exceptional |
21:23 | <rbuckton> | a statement is a statement. |
21:24 | <Bakkot> | I don't think that's a distinction users really think about. |
21:24 | <Bakkot> | `if` is a statement, but does something exprsesions can do |
21:24 | <Bakkot> | that's fine |
21:25 | <shu> | i can sympathize with the desire to check all the boxes for all the statements, even though i disagree |
21:25 | <shu> | i also have an additional reason to ban, which is i don't really think it's worth either spec authors' time nor implementers' time to work through the corner cases like loop headers |
21:27 | <Bakkot> | there's a lot of edge cases, yeah |
21:27 | <rbuckton> | Bad alternative: |
21:27 | <rbuckton> | ``` |
21:27 | <rbuckton> | const sContinue = Symbol(); |
21:27 | <rbuckton> | for (const x of ar) { |
21:27 | <rbuckton> | try { |
21:27 | <rbuckton> | const y = x.y ?? do { throw sContinue; } |
21:27 | <rbuckton> | } |
21:27 | <rbuckton> | catch (e) { |
21:27 | <rbuckton> | if (e === sContinue) continue; |
21:27 | <rbuckton> | throw e; |
21:27 | <rbuckton> | } |
21:27 | <rbuckton> | } |
21:27 | <rbuckton> | ``` |
21:27 | <Bakkot> | yeah, people can write bad code if they want |
21:28 | <shu> | so the desire to have universal availability of statements at least has a caveats that the corner cases need some explanation. we can of course define _something_, but i reckon in the end it'll be more surprising that not. we can selectively ban, but that's an equally careful task to carry out, one that i am not happy to allocate staff to |
21:28 | <Bakkot> | I don't find "people can use this bad alternative" a good reason to allow a thing |
21:28 | <Bakkot> | I do not expect people to actually write that in practice |
21:28 | <Bakkot> | instead of your first non-cf example |
21:28 | <shu> | you can do arbitrary non-structured control flow if you reloop your code today, is that a reason to add goto? |
21:29 | <shu> | i mean i would personally love goto |
21:29 | <rbuckton> | At least with banning we can relax that later if there is enough interest in community. I'm not going to die on the hill of `break/continue/return` in `do{}` as I don't have a strong opinion. |
21:29 | <jorendorff> | shu: as far as `return` is concerned, I don't think there are necessarily any new corner cases to work through; since `yield` already exists, anything to do with expressions returning has already been sorted |
21:29 | <shu> | jorendorff: i don't follow |
21:30 | <ljharb> | rbuckton: i think that you wouldn't be alone in jumping on board an immediate follow-on proposal to relax the ban |
21:30 | <rbuckton> | shu: The only `goto` I want would be to jump to a specific falling switch case (as in C#). |
21:30 | <rbuckton> | s/falling/following |
21:30 | <shu> | jorendorff: mechanically being like return doesn't mean programmers think about them similarly, and my impression is that they're not thought of similarly |
21:31 | <jorendorff> | shu: was in response to the specific point that it wasn't worth spec authors' or implementors' time to work through special cases |
21:31 | <rbuckton> | But in C#'s case it's `goto case ...` |
21:31 | <shu> | jorendorff: ah, okay, and for return you're saying that's not true |
21:31 | <Bakkot> | jorendorff theere is one new edge case, which is `return` in parameter expressions |
21:31 | <Bakkot> | but yeah it's not nearly as bad as `break` |
21:31 | <jorendorff> | Bakkot: fantastic |
21:32 | <jorendorff> | Bakkot: yield is simply banned there, suggesting the same for return |
21:32 | <Bakkot> | yeah, it's not too bad |
21:33 | <rbuckton> | Banning `break`/`continue`/`return` is purely banning syntax. If they weren't banned the existing spec machinery would already cover how their handled. The downside of banning them is that it makes the spec more complex rather than less. |
21:33 | <ljharb> | rbuckton: i don't think it would? because it could happen from a bunch of new places |
21:33 | <rbuckton> | You also have to ban break/continue/return in eval in a `do` |
21:33 | <jorendorff> | i'm not sure the spec says clearly what `for (do {break};;)` ought to do |
21:34 | <Bakkot> | rbuckton: yeah, part of the reason I'm advocating for just banning `break/continue/return`, instead of e.g. having `return` mean "provide this value from the `do`", as some have suggested, is so that the restriction can be relaxed later if there is enough community and implementor interest and someone works through all the edge cases |
21:34 | <jorendorff> | rather, i'm not sure it does what we would want |
21:34 | <Bakkot> | rbuckton they are already banned |
21:34 | <Bakkot> | you cannot `break` out of an `eval`, even a sloppy direct eval |
21:34 | <jorendorff> | or return, sads |
21:35 | <rbuckton> | fair |
21:35 | <Bakkot> | (presumably for the same reason as I am proposing not to let you `break` out of a `do` :P) |
21:36 | jorendorff | registers doubt |
21:36 | <rbuckton> | Bakkot: by that you mean this? `for (...) do { break; }`? |
21:36 | <rbuckton> | or this: `do { break; /*early exit from the do itself*/ }`? |
21:37 | <Bakkot> | first one |
21:37 | <rbuckton> | k. |
21:37 | <rbuckton> | heh: `do { it: break it; }` |
21:37 | <Bakkot> | that was one of my examples! |
21:37 | <Bakkot> | actually slightly more complicated |
21:38 | <TabAtkins> | ...can you `yield` within a parameter expression |
21:38 | <rbuckton> | banning break/continue is would just be changing how labeled evaluation works, correct? Or are you going to try to make `break`/`continue` syntactically invalid in the grammar? |
21:38 | <TabAtkins> | Ah, jorendorff seems to be saying they're banned there, ok |
21:39 | <shu> | rbuckton: i was imagining early error static semantics |
21:39 | <jorendorff> | i was also imagining EESS |
21:40 | <devsnek> | banning the control flow in loop headers and function parameters is fine |
21:40 | <rbuckton> | That's fine, just concerned about grammar complexity. |
21:40 | <Bakkot> | rbuckton the grammar is already set up not to allow an unlabeled `break` outside of a loop, or a `break l` out side of an `l` |
21:41 | <Bakkot> | making it so that there are no labels or loops visible to the `do` is actually easier than propagating that information through all expressions |
21:41 | <Bakkot> | (by a lot) |
21:41 | <devsnek> | not really |
21:41 | <devsnek> | you have to explicitly ban it using early errors |
21:41 | <rbuckton> | The grammar for `do {}`? Because there's no grammar restrictions in the spec: https://tc39.es/ecma262/#prod-BreakStatement |
21:42 | <devsnek> | or a parameter |
21:42 | <Bakkot> | devsnek you'd have to explicitly allow it, too |
21:42 | <devsnek> | no you wouldn't? |
21:42 | <Bakkot> | there is an early error for `break` outside of loops |
21:42 | <devsnek> | but not one for break inside blocks |
21:42 | <Bakkot> | to allow `for (;;) { (do { break;} ) }` you need to make the error not trip |
21:42 | <devsnek> | nothing explicitly allows `x: { break x; }` |
21:42 | <devsnek> | er |
21:43 | <devsnek> | `x: { { break x; } }` |
21:43 | <devsnek> | you don't need to add anything for `x: { do { break x; } }` to work there |
21:43 | <Bakkot> | yes, you do |
21:44 | <Bakkot> | you need to modify `ContainsUndefinedBreakTarget` to not error in that case |
21:44 | <Bakkot> | currently it would |
21:44 | <Bakkot> | (ok it might not, but it certainly would for e.g. `x: if(do{break x;}) ;` |
21:44 | <Bakkot> | ) |
21:44 | <rbuckton> | LabelledEvaluation doesn't apply to expressions, so the labelSet wouldn't be populated. |
21:44 | <Bakkot> | right |
21:44 | <Bakkot> | so |
21:44 | <Bakkot> | it would error |
21:44 | <devsnek> | ok true labelled evaluation would need to be updated |
21:45 | <Bakkot> | labelled evaluation is runtime semantics |
21:45 | <Bakkot> | you'd also need to update the static semantics |
21:45 | <devsnek> | indeed |
21:45 | <devsnek> | are you sure about the if statement? |
21:45 | <rbuckton> | devsnek: a different example might be this: `switch (x) { case 0: do {break; } }` since it would have passed the `ContainsUndefinedBreakTarget` check |
21:46 | <rbuckton> | Or `do { do { break; } } while (true);` ;) |
21:46 | <devsnek> | lol |
21:46 | <devsnek> | got it |
21:46 | <Bakkot> | devsnek I am not sure if it would error. But either it would error, or `x: if(do{break y}); ` would not. so it would need to be updated one way or the eother. |
21:47 | <Bakkot> | you'd definitely to propagate the about labels/breaks through expressions, where currently and with my proposed semantics you do not need to do that. |
21:47 | <devsnek> | i'll write up the changes for that if you want |
21:47 | <Bakkot> | so, yeah, like I said; it's actually strictly simpler to spec my propose semantics. not that this should have much weight |
21:47 | <Bakkot> | nah |
21:47 | <Bakkot> | I don't think spec difficulty should matter, like I say |
21:48 | <Bakkot> | this was just a response to rbuckton's concern about the spec difficulty of my proposed semantics |
21:52 | <jorendorff> | devsnek: Other iterator prototypes have a toStringTag, like <https://tc39.es/ecma262/#sec-%arrayiteratorprototype%-@@tostringtag>. |
21:52 | <jorendorff> | devsnek: is it intentional that %WrapForValidIteratorPrototype% does not? |
21:52 | <devsnek> | no |
21:53 | <jorendorff> | ok, i'll ask adam if he can file a PR to add it |
21:55 | <shu> | rbuckton: so i think i can be convinced that selective banning may be the desirable solution as a follow-on, especially if a version without control flow is shipped first |
21:55 | <shu> | and we get to see what the uptake is like |
22:47 | <jorendorff> | hmm. are do-expressions already in any of the compile-to-js languages? |
22:47 | <jorendorff> | curious to know what the uptake is like already |
22:48 | <jridgewell> | Babel? |
22:48 | <ljharb> | jorendorff: lots of us do a lot of strong advocacy for people to never use pre-stage-3 proposals, so it may not be that clear a picture |
22:48 | <leobalter> | @jorendorff jridgewell https://babeljs.io/docs/en/babel-plugin-proposal-do-expressions |
22:49 | <Bakkot> | babel's is broken-ish though |
22:50 | <Bakkot> | but still popular with jsx, I think |
22:50 | <jridgewell> | Yah, I think JSX expressions is the biggest usecase |
22:51 | <jridgewell> | [Repl](https://babeljs.io/repl#?browsers=ie%2011&build=&builtIns=false&spec=false&loose=true&code_lz=MYewdgzgLgBCMF4YBN4G8BQMYEsBmMAFFAE4CuApgJQybbamVYwC-MFANhBbc9ngEMuFZiwwsgA&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=true&fileSize=false&timeTravel=false&sourceType=module&lineWrap=true&presets=stage-1&prettier=false&targets=&version=7.10.2&externalPlugins=) |