00:01 | <TabAtkins> | `let value = do { let t; for(const v of values) { if(pred(v)) { t=v; break; } }; t}` |
00:01 | <devsnek> | i don't hate having to do that |
00:01 | <devsnek> | but i much prefer using the completion value |
00:01 | <TabAtkins> | vs `let value = do { for(const v of values) if(pred(v)) break with v; };` |
00:02 | <devsnek> | actually break with and continue with are good ideas |
00:02 | <ljharb> | oof |
00:02 | <devsnek> | no no wait |
00:03 | <devsnek> | loops complete with undefined unless you use `break with` |
00:03 | <devsnek> | continue with makes no sense pretend i didn't say that |
00:03 | <ljharb> | `break with` is at least explicit |
00:04 | <devsnek> | seems we have options |
00:04 | <Bakkot> | options are kind of the problem because we don't have a good way to choose between them |
00:04 | <devsnek> | options which aren't blocking each other |
00:05 | <Bakkot> | I am hoping there is a minimal subset which just bans everything people disagree about and is still useful for some cases |
00:05 | <rkirsling> | is there a demand for `return` to refer to the function scope though? |
00:05 | <devsnek> | yes |
00:05 | <devsnek> | that's my primary use case |
00:05 | <rkirsling> | oh. |
00:06 | <devsnek> | lol |
00:06 | <ljharb> | it would be exceedingly confusing for `return` to not mean that imo |
00:07 | <devsnek> | to me it just seems |
00:07 | <devsnek> | you have some logic |
00:07 | <rkirsling> | depends on how you're conceiving of this magical statement-to-expression space |
00:07 | <devsnek> | you refactor it to use a do expression |
00:08 | <devsnek> | now all of a sudden you have to restructure your control flow |
00:08 | <devsnek> | because you can use break/continue/return inside it |
00:08 | <devsnek> | can't* |
00:08 | <rkirsling> | hmm that's not a bad point |
00:09 | <rkirsling> | ugh the further this discussion goes on though, the more unsure I become about the feature |
00:09 | <rkirsling> | like, I love "everything's an expression" but |
00:10 | <rkirsling> | does that imply that I actually have a convincing use case for "a block that turns all your imperative logic into an expression when all is said and done"? |
00:10 | <devsnek> | i would use it all over the place to structure things |
00:11 | <devsnek> | it's like the big brother of empty lines between things |
00:11 | <rkirsling> | as like, local functions without call overhead? |
00:11 | <devsnek> | i mean like |
00:12 | <devsnek> | when you have a bunch of lines of code, you generally group them with empty lines |
00:12 | <devsnek> | often in rust people also scope them using block expressions |
00:12 | <devsnek> | and i really like that pattern |
00:12 | <rkirsling> | wait what why |
00:12 | <rkirsling> | why waste syntax if it's no better than newlines |
00:13 | <rkirsling> | oh ,you're saying like |
00:13 | <devsnek> | irc needs multiline support |
00:13 | <rkirsling> | "there's no need for this to go out of scope yet but doing so clarifies the fact that I'm only using it *right here*" |
00:14 | <devsnek> | yeah |
00:14 | <devsnek> | its just clean |
00:14 | <devsnek> | you end up at the end with just the variables you're actually using |
00:14 | <rkirsling> | and then said "paragraph" or "stanza" or whatever you wanna call it _has_ a result |
00:14 | <devsnek> | oh eah |
00:14 | <devsnek> | yeah |
00:14 | <devsnek> | they produce results |
00:14 | <rkirsling> | so that'd give the block purpose in Rust |
00:14 | <rkirsling> | but in JS we'd need a new thing |
00:14 | <rkirsling> | okay |
00:15 | <devsnek> | yeah in rust you can do `let x = { 5 };` |
00:15 | <rkirsling> | that is, while abstract, not unconvincing |
00:15 | <rkirsling> | right |
00:15 | <devsnek> | rust has way better semantics around that though |
00:15 | <rkirsling> | so then even like |
00:16 | <devsnek> | https://gc.gy/57811604.png |
00:17 | <rkirsling> | const result = do { |
00:17 | <rkirsling> | const myNamedBoolArg = true; |
00:17 | <rkirsling> | foo(data, myNamedBoolArg); |
00:17 | <rkirsling> | }; |
00:17 | <devsnek> | yeah you could do that |
00:18 | <rkirsling> | kinda superfluous but yeah, documents itself, perhaps |
00:19 | <devsnek> | honestly the most op pattern is if expressions |
00:19 | <devsnek> | we could replace do expressions with `if (true) {}` if expressions :P |
00:19 | <rkirsling> | I kind of resent Rust's lack of ternary but yeah, if you have any nontrivial branches then if exprs are pretty darn great |
00:20 | <rkirsling> | lol |
00:23 | <Bakkot> | anyway so: does anyone here _object_ to, do expressions, where you cannot have your last statement be a loop or declaration, and break/return/continue are banned? |
00:23 | <Bakkot> | I know this does not meet devsnek's use case |
00:24 | <Bakkot> | but hope that it is useful enough anyway |
00:24 | <devsnek> | if I represented myself I would object |
00:25 | <Bakkot> | on what basis? |
00:25 | <devsnek> | on not having control flow |
00:26 | <Bakkot> | I know it doesn't meet your use case; is your position that it is not worth having if it does not meet your use case? |
00:26 | <devsnek> | I'm imagining a lot of humans would want to use it |
00:26 | <Bakkot> | it's not like adding this without allowing break/return prevents relaxing that restriction later |
00:26 | <devsnek> | given my refactoring point above |
00:26 | <devsnek> | so I'm worried it wouldn't be useful enough without control flow |
00:27 | <Bakkot> | I personally would still find it very useful |
00:27 | <devsnek> | maybe someone with a zillion followers on twitter can make a poll |
00:28 | <rkirsling> | I think it's worth presenting |
00:28 | <Bakkot> | I think most of the pipeline proposal's use cases would be met by this, also |
00:28 | <rkirsling> | oh god not a Twitter poll |
00:28 | <devsnek> | seems useful for pipeline yeah |
00:28 | <Bakkot> | also to do `let x = try { foo() } catch { null }` which is very common |
00:28 | <Bakkot> | ime |
00:29 | <devsnek> | if we get do expressions do we drop the throw expression proposal |
00:29 | <rkirsling> | tbh if we go back to the pipeline discussion though and the refactorings TabAtkins was showing above, I really do think the `#`-chasing leads to comprehensibility |
00:30 | <devsnek> | if the spec requires the hash to be bright red when displayed |
00:30 | <rkirsling> | :stare: |
00:30 | <devsnek> | Bakkot: I'd feel better about it if pipelines advance |
00:31 | <Bakkot> | devsnek: ... why? |
00:31 | <devsnek> | but like I said I don't think I'll actually block either way |
00:31 | <Bakkot> | I'm mostly interested in this as an _alternative_ to pipelines |
00:31 | <devsnek> | because people won't use control flow in them |
00:31 | <devsnek> | oh interesting |
00:32 | <devsnek> | yeah I guess that was mentioned above |
00:32 | <Bakkot> | like instead of `x = a |> b(0, #) |> await #` ro whatever, you'd write `x = do { let $=a; $=b(0, #); await #}` |
00:32 | <Bakkot> | s/ro whatever/or whatever/ |
00:32 | <Bakkot> | also s/#/$/g |
00:32 | <rkirsling> | but I kinda thought avoiding locals was the point |
00:32 | <devsnek> | are there real world examples of that |
00:33 | <devsnek> | HOC with await or something |
00:33 | <rkirsling> | (not the point of the proposal but the reason why existing approaches don't suffice, I mean) |
00:34 | <Bakkot> | rkirsling I think the main reason for avoiding locals is so that you can remain in expression position, and also not pollute your local scope |
00:34 | <Bakkot> | some people (pointfree people) have a principled objection to locals, but I don't think that's the dominant party |
00:34 | <rkirsling> | hmm okay |
00:34 | <rkirsling> | I do see your point |
00:39 | <Bakkot> | devsnek: `console.log(filter(parse(await fetch(extractRemoteUrl(await readDB(makeQuery('some query'))))));` is the sort of thing I write a lot |
00:39 | <Bakkot> | well, obviously I break it up into multiple lines with locals |
00:39 | <Bakkot> | but you get the idea |
00:39 | <Bakkot> | not HOCs or whatever |
00:39 | <Bakkot> | just normal chasing data around |
00:40 | <devsnek> | is that code real |
00:40 | <TabAtkins> | it looks *plausible*, at least |
00:40 | <TabAtkins> | I've written code like that ^_^ |
00:40 | <devsnek> | I've never seen code like that |
00:40 | <devsnek> | I don't think |
00:40 | <Bakkot> | it is not literally copied from one of the proprietary code bases I work on, no |
00:41 | <devsnek> | if things like it exist I'll believe you |
00:41 | <Bakkot> | but it is very much the sort of thing I write in them, except I would make intermediate variables so a human could read it |
00:41 | <TabAtkins> | my python instincts these days would probably force me to break that up into multiple assignments, but still |
00:42 | <devsnek> | I just have never seen them so i would like to get a sense of what real world code looks like |
00:50 | <rkirsling> | agree that it looks plausible |
00:50 | <shu> | Bakkot: are you taking up do expressions again |
00:50 | <rkirsling> | (regardless of whether it'd get past code review, tehe) |
00:52 | <Bakkot> | shu: maybe |
00:52 | <shu> | okay |
00:52 | <Bakkot> | shu: mostly I would like them discussed if we talk about pipelines again |
00:52 | <shu> | i do not think pipelines are a good idea currently |
00:52 | <Bakkot> | because I think they are better than pipelines and also mean you don't need pipelines |
00:52 | <shu> | great |
00:57 | <devsnek> | shu: what do you think about control flow in do expressions |
00:58 | <shu> | control flow that breaks out of do expressions? |
00:58 | <shu> | like a return in a do expression? |
00:58 | <devsnek> | yeah |
00:59 | <devsnek> | return returns the outer function that is |
01:00 | <shu> | that seems like a bad idea |
01:00 | <devsnek> | :( |
01:00 | <shu> | what's the motivation for having that behavior? |
01:02 | <shu> | oh i see some backlog, you're worried about a refactoring hazard? |
01:02 | <devsnek> | yeah |
01:02 | <devsnek> | i mean i also have a use case for returning in them |
01:02 | <devsnek> | but more generally the refactoring hazard seems annoying |
01:03 | <devsnek> | i can imagine code like `let x; try { x = z() } catch { break }` exists |
01:03 | <devsnek> | or similar with if/else |
01:03 | <shu> | my opinion is those patterns are more harm than good |
01:04 | <shu> | those patterns = to expect to wrap them in an expression and keep the same behavior |
01:04 | <devsnek> | yeah i mean |
01:04 | <devsnek> | adding a block around code doesn't change control flow |
01:05 | <devsnek> | i don't get why adding `let x = do` at the front of the block is so controversial |
01:05 | <shu> | why is that comparable here? |
01:05 | <devsnek> | aside from the break inside while loop head |
01:05 | <devsnek> | i get why that is weird |
01:05 | <devsnek> | s/while loop/loop/ |
01:06 | <shu> | because in an algol-like language there are certain (fairly strong imo) properties that tend to hold of statements and expressions |
01:06 | <shu> | and one of them is generally that expressions evaluate beginning to end without affecting the control flow of the surrounding context, whatever that is |
01:06 | <devsnek> | i've never used algol |
01:06 | <shu> | sometimes you have exceptions that aren't too confusing |
01:07 | <shu> | well, c-like then |
01:07 | <devsnek> | i guess |
01:07 | <devsnek> | i don't see any strong reason to have a separation |
01:07 | <shu> | dunno what to tell ya |
01:07 | <shu> | most people i think do |
01:07 | <devsnek> | like some things obviously don't make sense as expressions |
01:08 | <devsnek> | like variable declarations |
01:08 | <devsnek> | what property do you get from strong separation of expressions and control flow |
01:09 | <shu> | the property of not having to think about control flow if i'm reading an expression position |
01:09 | <shu> | anywho gotta run |
01:09 | <devsnek> | aight |
01:11 | <shu> | fwiw i think the property i like is a little stronger than the expression/statement split, it's more about having clear scopes where control flow has effect |
01:11 | <shu> | e.g. break doesn't work in expression contexts now, to suddenly make it work is a very big ask |
01:11 | <rkirsling> | I can agree insofar as control flow _within_ seems okay, it's control flow _through_ that gives pause |
01:11 | <shu> | right |
01:12 | <shu> | put another way, control flow in JS is delimited in certain ways today |
01:12 | <shu> | to change that is a *huge* ask and break in mental model that i'd fight against |
01:12 | <shu> | in languages with undelimited continuations, that shit is hard to wrap your head around for similar reasons |
01:12 | <devsnek> | i don't quite understand what that mental model is |
01:12 | <shu> | and why people end up using delimited continuations |
01:12 | <devsnek> | maybe i'll read some algol stuff |
01:13 | <shu> | i don't know if algol is a good starting point |
01:13 | <devsnek> | i just really don't understand the separation of statements and expressions |
01:14 | <shu> | but i'm pretty sure you have a mental model of what return does, right? the mental experiment i recommend is 1) ask if you also think presence of call/cc or setjmp/longjmp make reasoning about return hard, and 2) if you find the difficulty comparable, but to a lesser degree, with allowing returns in expressions |
01:15 | <devsnek> | what is cc |
01:15 | <Bakkot> | https://en.wikipedia.org/wiki/Call-with-current-continuation |
01:15 | <Bakkot> | one of the other major paradigms for control flow |
01:16 | <shu> | that's probably not a good example, return-in-expressions aren't undelimited |
01:16 | <devsnek> | well i will say |
01:16 | <shu> | look at shift/reset instead |
01:16 | <devsnek> | reasoning about longjmp is very difficult |
01:20 | <rkirsling> | is it funny that I know about call/cc but am having to look up longjmp |
01:21 | <rkirsling> | (I mean it's sort of what I was expecting but still) |
01:22 | <devsnek> | i knew about longjmp but not call/cc |
01:22 | <devsnek> | call/cc is weird |
01:22 | <devsnek> | i find call/cc and longjmp to be a completely different universe of complexity compared to returning from inside a do expression |
01:23 | <Bakkot> | call/cc is yield on steroids, basically |
01:23 | <devsnek> | idk why all the websites have to make it sound so complex |
01:23 | <devsnek> | well i mean it is complex |
01:24 | <devsnek> | but i feel like these concepts are always explained in the most difficult terms instead of the easiest terms |
01:24 | <devsnek> | in any case, call/cc and longjmp are both dynamic |
01:25 | <devsnek> | and involve non-local control flow |
01:25 | <devsnek> | i don't really see a comparison between them and allowing break/return/continue inside do expressions |
01:30 | <rkirsling> | hah, good old Oleg |
01:30 | <rkirsling> | https://en.wikipedia.org/wiki/Call-with-current-continuation#Criticism |
01:30 | <devsnek> | call/cc makes sense if you don't use the word "continuation" |
01:31 | <devsnek> | its like when people explain monads |
01:40 | <rkirsling> | I usually just think of "continuation" just as "return target" but then it makes "current continuation" a bit more confusing |
01:41 | <devsnek> | i posted the block-expression control flow vs call/cc thing in a programming language design discord |
01:42 | <devsnek> | people are angry that they would be equated |
01:50 | <shu> | i did not equate them |
01:51 | <devsnek> | i don't understand what the comparison is |
01:52 | <shu> | the point was a broad one: you have expectations when reading "return" of what it does, and those expectations must be thrown out when you see setjmp/longjmp |
01:52 | <shu> | similarly, to a lesser degree, people's expectations of "return" will need to be re-calibrated if they are allowed in do expressions in JS |
01:53 | <devsnek> | aside from "i don't expect there to be a return value in this syntactic position (because that's true of any statement)" what is the expectation there |
01:53 | <shu> | as i said before, that expressions do not affect control flow |
01:53 | <rkirsling> | (wow I forgot that you can't throw from a ternary in JS) |
01:54 | <devsnek> | throw expressions is a proposal |
01:54 | <devsnek> | shu: out of curiosity have you used any languages with this feature before |
01:54 | <devsnek> | like rust or smth |
01:55 | <shu> | i have used rust, but an earlier version and not since 1.0 i don't think |
01:55 | <rkirsling> | like I was saying though, I think it's very different when "everything is a statement" |
01:55 | <rkirsling> | dammit |
01:55 | <rkirsling> | s/statement/expression/ |
01:55 | <devsnek> | also i'm curious about if yield and await violate your expectations |
01:55 | <rkirsling> | like, the thing that creates confusion or weird expectations is the do block |
01:56 | <rkirsling> | we have this barrier between worlds and it's tricky to decide upon its properties |
01:56 | <rkirsling> | if there's no barrier, there's nothing to be confused about |
01:56 | <devsnek> | i just don't understand this expectation or why maintaining it matters |
01:57 | <devsnek> | i don't think i've ever seen a rust user be confused about it anyway |
01:57 | <devsnek> | maybe that's because they're too busy being confused about lifetimes |
01:57 | <rkirsling> | there's nothing _to be confused about_ in Rust |
01:57 | <devsnek> | wdym |
01:58 | <rkirsling> | `do { ... }` raises the question of "just how much is this thing like a function scope" |
01:58 | <devsnek> | i don't understand why it does |
01:58 | <devsnek> | https://www.irccloud.com/pastebin/aJYvqDl4/x.rs |
01:58 | <devsnek> | so here's an example from rust |
01:59 | <shu> | devsnek: yield and await do not violate my expectations, since they produce values usable as expressions |
01:59 | <devsnek> | they don't have to |
01:59 | <devsnek> | they might never return |
01:59 | <rkirsling> | devsnek: if you shorten it to that extent then it could go either way though |
01:59 | <shu> | devsnek: right? |
02:00 | <devsnek> | how is that control flow different |
02:00 | <shu> | than return and break, which transfer control and do not return a value? |
02:00 | <devsnek> | yield and await both transfer control |
02:00 | <shu> | yield and return abstract their control flow in such a way that should execution resume, you're back at where you were, with a new value (that you may use or not) |
02:01 | <devsnek> | and might not return a value |
02:01 | <shu> | err |
02:01 | <shu> | yield and await |
02:02 | <devsnek> | shu: what if i told you return could resume but it never does |
02:02 | <shu> | i don't understand what that means |
02:02 | <bradleymeck> | it would still fire the finally around it though? |
02:02 | <devsnek> | why does being able to resume change the control-flow-ness of an operator |
02:02 | <bradleymeck> | try { return } finally { ... } |
02:03 | <devsnek> | bradleymeck: talking about whether control flow should be allowed in do expressions btw |
02:03 | <bradleymeck> | i think its just a mental model of the control flow being a valid expression itself |
02:03 | <bradleymeck> | foo(return) doesn't make any sense, and that gets confusing in foo(do { return }) |
02:04 | <devsnek> | if return parsed as an expression it could make sense |
02:05 | <bradleymeck> | it could parse in the same position but it would never have a value generated for that position |
02:05 | <devsnek> | right its a never type |
02:05 | <bradleymeck> | i don't actually care about such a thought myself, but it seems to come up |
02:05 | <devsnek> | same as a function that always throws |
02:06 | <bradleymeck> | but thats in a different "frame/scope", so often not thought of when reading the code (even if it is a valid point) |
02:07 | <shu> | devsnek: i feel like you are not receptive to what i have said, and that's okay. you asked what i thought of control flow in do expressions |
02:07 | <devsnek> | shu: no i just want to understand |
02:07 | <bradleymeck> | i think control flow in do expressions is fine-ish, finally {} can get confusing if you cancel ending the frame |
02:07 | <devsnek> | sorry if that didn't come across well |
02:08 | <devsnek> | finally is a good point about how people can understand odd control flow |
02:08 | <shu> | it seems like you don't have any different expectations of expressions and statements wrt control flow |
02:08 | <shu> | i do |
02:09 | <shu> | the existence of odd control flow in the language now doesn't have generalize to "therefore allowing them in expressions doesn't change expectations" |
02:09 | <bradleymeck> | my only concern is from half finished statements: `while(...) { foo(sideEffect, do {continue}); }` currently statements are the points to observe non-throw control flow |
02:09 | <devsnek> | if that's all it comes down to i would say keeping that expectation doesn't matter that much |
02:09 | <bradleymeck> | so, you have to walk expressions for throws but not for other things |
02:10 | <shu> | await being an expression because it needs to return a value did require some extra restrictions |
02:10 | <shu> | like disallowing it in parameter expressions |
02:10 | <shu> | devsnek: that's an opinion, not an argument? |
02:14 | <devsnek> | shu: right i meant, there's no point in trying to draw a conclusive decision from just an opinion |
02:14 | <shu> | who... is trying to do that? |
02:15 | <devsnek> | well i was trying to do that |
02:15 | <devsnek> | until you clarified |
02:15 | <devsnek> | i was trying to get to a point where i could make a decision based on what you were saying |
02:17 | <shu> | i missed that context, thought you just wanted my thoughts. a decision on? |
02:18 | <devsnek> | i was trying to figure out the importance of control flow in do expressions |
02:18 | <devsnek> | or lackthereof |
02:19 | <shu> | it seemed like your own opinion is pretty strong already, that they should be allowed |
02:20 | <bradleymeck> | i'd be equally curious of why we couldn't enable control flow later. though i bet in general just banning them directly inside of params wouldn't be terrible |
02:25 | <devsnek> | you don't really have to explicitly ban them inside params or anything |
02:26 | <devsnek> | like theoretically loop heads and function params are already barriers to their relevant control flow |
02:29 | <Bakkot> | devsnek: is the PL discord public? |
02:30 | <devsnek> | Bakkot: #lang-dev on the rust community discord |
02:30 | <devsnek> | (a channel about developing weird programing languages using rust, not about the development of the rust language) |
02:31 | <Bakkot> | thank |
02:32 | <Bakkot> | anyway I am going to be proposing do expressions with control flow disallowed |
02:32 | <Bakkot> | since I think everyone agrees on that subset |
02:32 | <Bakkot> | someone with more willpower than me can fight the control flow fight later |
02:32 | <rkirsling> | all of it? or just abrupt completions? |
02:33 | <Bakkot> | rkirsling break/return/continue crossing the do{}, specifically |
02:33 | <rkirsling> | cool |
02:33 | <Bakkot> | those are the only things people fight about |
02:33 | <rkirsling> | yes |
02:34 | <rkirsling> | if and try seem enough for a presentation |
02:39 | <ljharb> | i missed a log, but do expressions do not obviate the need for pipelines for me. |
02:39 | <ljharb> | they would be a solution, but not an ergonomic one for the use cases i have. |
02:40 | <Bakkot> | ljharb do you have examples of those use cases? |
02:41 | <ljharb> | i have the HOC one above |
02:41 | <ljharb> | those are in modules where the result is just export defaulted; local vars would work fine, but that looks way worse than the )))) versión already, let alone the pipeline one |
02:41 | <devsnek> | oh wow someone's proposing spaceship operator |
02:41 | <ljharb> | the desire is to have a chain of functions, just like you’d have a chain of OO methods |
02:42 | <ljharb> | do expressions only satisfy the “statements in expression position” part, which imo is not something i care about for pipeline |
02:42 | <ljharb> | devsnek: Hemanth and i yes |
02:42 | <Bakkot> | ljharb hmmm |
02:42 | <ljharb> | devsnek: well, we’re proposing that we solve a problem. Spaceship is just what we think solves it best ;-) |
02:43 | <devsnek> | bradleymeck: for the arbitrary module names slides, you can have cjs facades, even autogenerated |
02:44 | <devsnek> | ljharb: you should lead with s/a - b/a <=> b/ for array sorting |
02:53 | <Bakkot> | I do not understand the relationship between <=> and Array.prototype.compare |
03:01 | <ljharb> | Bakkot: the latter is comparison for arrays. The former is a comparison protocol for everything. |
03:02 | <ljharb> | if the array method and the operator both exist, then I’d expect the method to delegate to the symbol on array.prototype |
03:04 | <devsnek> | there's a standard c extension which allows expression blocks |
03:05 | <devsnek> | and it allows control flow |
03:05 | <Bakkot> | it also allows crashing compilers pretty regularly |
03:05 | <Bakkot> | c has a lot of extensinos |
03:05 | <Bakkot> | *extensinos |
03:05 | <Bakkot> | ugh |
03:05 | <Bakkot> | woords |
03:05 | <Bakkot> | wooooooords |
03:05 | <Bakkot> | *extensions |
03:05 | <Bakkot> | if you're looking for an example which allows control flow, ruby's blocks are probably more relevant |
03:06 | <devsnek> | so far every example i find allows it |
03:06 | <ljharb> | every language allows reflection on private things too :-) we can be better |
03:07 | <Bakkot> | most languages just don't allow statements in expression position at all |
03:07 | <devsnek> | yeah but people don't say its a mistake |
03:07 | <Bakkot> | the languages which do tend to have that as their philosophy, so it makes sense to allow control flow there also |
03:07 | <Bakkot> | we are grafting this feature onto a language which already has an expression/statement dichotomy |
03:07 | <Bakkot> | so our situation is somewhat unique |
03:07 | <devsnek> | c has the same issue |
03:08 | <devsnek> | point taken about crashes but still |
03:08 | <Bakkot> | I would seriously not use GCC-specific extensions as precedent for anything ever |
03:08 | <Bakkot> | they are 100% added because they solve a specific use case someone had, not coherently designed to be a reasonable feature |
03:08 | <rkirsling> | ^ |
03:08 | <devsnek> | clang supports it too |
03:09 | <rkirsling> | I mean that's not unusual though |
03:09 | <rkirsling> | clang strives to be GCC compatible |
03:09 | <devsnek> | msvc doesn't support it |
03:09 | <devsnek> | but msvc is also kind of broken in general |
03:12 | <rkirsling> | I mean...maybe so but I don't think this exemplifies that in any way |
14:59 | <ystartsev> | devsnek: will you be doing an update on iterator helpers? |
15:03 | <devsnek> | ystartsev: no :( |
15:04 | <devsnek> | ystartsev: did you see the conversation in #tc39-implementers |
15:04 | <ystartsev> | devsnek: since we are starting to implement, it would be great to have a discussion about the generator stuff you and jorendorff were trying to figure out |
15:04 | <ystartsev> | yes |
15:04 | <devsnek> | ok cool |
15:04 | <ystartsev> | would you be ok if i started a conversation about that? |
15:05 | <devsnek> | wdym by conversation |
15:13 | <ystartsev> | devsnek: primarily, a clarification of the positions of the editors and the champion (so, you) regarding how generators should be used in the spec |
15:13 | <ystartsev> | this is so that we can move forward with the implementation. it wouldn't be stage advancement, just discussion |
15:13 | <devsnek> | oh i meant like what medium |
15:13 | <devsnek> | plenary discussion? |
15:13 | <ystartsev> | oh, as an agenda topic |
15:13 | <ystartsev> | yep |
15:14 | <devsnek> | sure, sound fun |
15:14 | <devsnek> | sounds* |
15:14 | <ystartsev> | cool. i will add it |
15:14 | <ystartsev> | and maybe make some slides based on your discussion with jorendorff |
15:14 | <devsnek> | 👍🏻 |
15:14 | <devsnek> | thanks for working on this |
15:15 | <ystartsev> | sure, happy to help! |
18:47 | <bradleymeck> | can anyone give my eyes some help, i wrote up https://gist.github.com/bmeck/5f195c4ae08009db4f3eefdc8bb360c9 to see if anything is using Symbol.species on their pages for Arrays and TypedArrays, but *nothing* is hitting that code except a coreJS eager feature detection. I feel like I definitely did something wrong |
18:53 | <ystartsev> | bradleymeck: it should work for this example right? |
18:53 | <ystartsev> | https://www.irccloud.com/pastebin/KlTA1VIh/ |
18:54 | <bradleymeck> | yea, i see the bug I hit |
18:55 | <bradleymeck> | coreJS makes species a regular function not a constructor so when I was patching that to figure out the noise ratio i broke everything else |
18:55 | <bradleymeck> | `species().foo` should be in a try catch |
18:55 | <bradleymeck> | well technically we could always use new? |
18:56 | <bradleymeck> | ystartsev: the goal is that we detect that code, yea |
18:57 | <bradleymeck> | well that we detect when the species in the subclass is not the builtin* |
18:58 | <bradleymeck> | cause if the behavior changes to always use the builtin we aren't concerned with the subclass species being the builtin |
18:58 | <ystartsev> | yes |
18:59 | <ystartsev> | sorry the code snippit was wrong, shouldn't be set to array |
18:59 | <bradleymeck> | thanks for the sanity glance |
19:03 | <ystartsev> | its hitting for me, ill post the snippit in case i did something wrong |
19:04 | <ystartsev> | can you see this? https://gist.github.com/codehag/2aaa571b6286f7f2cc28d01736c7e708 |
19:04 | <bradleymeck> | i'm just skeptical of these sites that are extending builtins via some regexps showing things like `extends Array` but never hitting any species usage |
19:04 | <bradleymeck> | i can see it, let me run it |
19:04 | <ystartsev> | one thing that is surprising to me is that we are not seeing angular show up |
19:05 | <ystartsev> | https://github.com/angular/angular/commit/58b29f1503a180fdfb8feb73a30d0c4448afad9a |
19:05 | <ystartsev> | i feel like we should see that? |
19:06 | <bradleymeck> | yea that gist hits the trap |
19:06 | <ystartsev> | oh! good |
19:06 | <ystartsev> | oh wait, my gist, yea that makes sense |
19:06 | <bradleymeck> | idk if angular hits it, but if you have a specific site i can visit to verify that would be good |
19:07 | <ystartsev> | lemme look |
19:07 | <bradleymeck> | angular vs angularjs still confuses me a bit |
19:08 | <bradleymeck> | ystartsev: i did disable Promise detection is likely why? I can re-enable it |
19:09 | <ystartsev> | this project hits it... don't know much about it |
19:09 | <ystartsev> | https://github.com/microsoft/ApplicationInsights-node.js |
19:10 | <ystartsev> | or this is better: https://zonejs-basic.stackblitz.io/ |
19:11 | <shu> | i can't understand the angular code |
19:12 | <shu> | it seems to be trying to follow the @@species protocol but doesn't depend on the native Promise.then, since it also patches then |
19:13 | <bradleymeck> | ystartsev: that page does not fire it, i've been trying to poke it to see if the detection is slightly off |
19:14 | <bradleymeck> | it doesn't use the builtin at least |
19:15 | <ystartsev> | interesting |
19:15 | <ystartsev> | might be a good example though, shows how species is not being used as expected anyway |
19:16 | <shu> | i have no problems with user libraries themselves using @@species as their own subclassing machinery |
19:16 | <ystartsev> | this is another example, they have a few https://stackblitz.com/edit/zonejs-throttle?file=index.js |
19:16 | <shu> | they don't have the implementation tradeoffs of native engines |
19:16 | <ystartsev> | yeah it looks like that is pretty much what angular did |
19:16 | <bradleymeck> | out of 1k sampled sites out of 400k, all 26 traps were from coreJS so i was losing my mind |
19:17 | <ystartsev> | shu: you mentioned that we should formulate a bigquery for this |
19:17 | <shu> | ystartsev: bradleymeck has already done great work there |
19:17 | <ystartsev> | oh great |
19:17 | <ystartsev> | ok, because i wasn't sure where to start |
19:17 | <shu> | ystartsev: though if you want to start playing with it, either send *me* queries to run, or wait until i can figure out how to get you GCP credits |
19:17 | <shu> | because it turns out it is expensive af |
19:17 | <ystartsev> | i have no idea how to forumate them, i would need to do some reading |
19:17 | <bradleymeck> | i have the output if you want, but its mostly just 20mb of pages in a CSV we need to actually validate against |
19:17 | <shu> | ystartsev: it's SQL |
19:18 | <shu> | that may not solve your problem :) |
19:18 | <ystartsev> | oh, thats straight forward |
19:18 | <shu> | i don't really know SQL, so that was my problem |
19:18 | <ystartsev> | its basically prolog but all caps right? |
19:18 | <devsnek> | select * from websites where breakage=true |
19:18 | <devsnek> | it doesn't have to use caps i don't think |
19:19 | <ystartsev> | bradleymeck if you have the data and the queries i would love to take a look |
19:19 | <ystartsev> | maybe we can make a doc about it for how-we-work |
19:20 | <bradleymeck> | ystartsev: DO NOT RUN the query I'm about to post, it is a bit pricey we found out |
19:20 | <ystartsev> | the angular story is really interesting because it tells us that people don't subclass the way we thought they would, and it gives more of a reason to remove this |
19:20 | <ystartsev> | :| gotcha |
19:20 | <bradleymeck> | https://www.irccloud.com/pastebin/Oyd05gui/dont_run_this |
19:20 | <bradleymeck> | we have saved output of it |
19:20 | <ystartsev> | cool |
19:21 | <ystartsev> | ah gosh i need to stop working on this, its super late |
19:21 | <ystartsev> | i am going to _call it a day_ |
19:21 | <ystartsev> | but i am very excited about this |
19:21 | <bradleymeck> | cya! enjoy the break |
19:21 | <devsnek> | how big is the dataset |
19:22 | <bradleymeck> | not as huge as I thought, our first naive query was like 6k and this one is up at around 400k |
19:22 | <bradleymeck> | after you dedup the page hrefs |
19:42 | <shu> | bigquery should rename FULL JOIN to EXPENSIVE JOIN |
19:59 | <bradleymeck> | shu we got another coreJS noise flag apparently with https://github.com/aldehydkrotonowy/source-code/blob/6bf6ff94a485bedb02218978a62dd2881c0c54d6/core-js/packages/core-js/internals/fix-regexp-well-known-symbol-logic.js#L22 |
20:00 | <bradleymeck> | gah, linked to wrong line, but yea that file also does eager detection |
20:03 | <bradleymeck> | https://github.com/zloirock/core-js/blob/master/packages/core-js/internals/fix-regexp-well-known-symbol-logic.js#L80 is the actual line, reading |
20:05 | <devsnek> | this code shouts too much |
20:14 | <bradleymeck> | shu: it looks like if the detection fails it replaces the builtins with its own there |
20:19 | <bradleymeck> | it is odd though, i only saw it because they use {} and call ({})[RegExp.prototype[Symbol.split]]('') |
20:20 | <bradleymeck> | https://github.com/zloirock/core-js/blob/master/packages/core-js/internals/fix-regexp-well-known-symbol-logic.js#L70-L73 sets the species for the ordinary object, but idk why, they don't seem to check the return value to be the same |
21:03 | <shu> | bradleymeck: i am on PTO today since Google gave everyone off, let's pick it up on tuesday. feel free to send me a gist or an email with the cases you find |
21:03 | <bradleymeck> | k |
21:08 | <bradleymeck> | another trap fires for Promise.then in https://github.com/zloirock/core-js/blob/717ff8bca8e68508ef6cef7eb673d0b39265739a/packages/core-js/modules/es.promise.js#L76 it seems |
21:09 | <shu> | a false positive or? |
21:10 | <bradleymeck> | unclear... the code is really abstract everytime i have to cross ref all these files for corejs |
21:10 | <bradleymeck> | it looks like it replaces Promise if it is wrong |
21:11 | <bradleymeck> | we likely should make a test/script to just punch away the globals ourselves and see what breaks |
21:11 | <Bakkot> | "see what breaks" is tricky, turns out |
21:11 | <Bakkot> | since almost all pages are throwing random errors all the time |
21:12 | <devsnek> | maybe a custom build of firefox or chrome that has prints in these @@species sections |
21:12 | <Bakkot> | and also have try-catch suppressing half of the errors |
21:12 | <devsnek> | anyone with sentry on their page |
21:12 | <devsnek> | will slurp up errors without you ever seeing them |
21:14 | <bradleymeck> | Bakkot: I'm more concerned with only the scope of these coreJS detection mechanisms |
21:14 | <bradleymeck> | the whole page is too big to reason about |
21:15 | <bradleymeck> | i'm already patching stuff in devtools, idk, you can't slurp up errors without me seeing them |
21:16 | <bradleymeck> | so far, all things i've read in the output are from coreJS |
21:16 | <bradleymeck> | which is kind of impressive? |
21:16 | <bradleymeck> | but it replaces builtins with its own stuff |
21:17 | <devsnek> | is it babel that inserts corejs |
21:20 | <ljharb> | often |
21:20 | <ljharb> | but not always |
21:30 | <bradleymeck> | a lot of these though are semi-easy to detect, like they construct a promise subclass that has fulfill and reject as the same function identity |
21:31 | <bradleymeck> | the real issue is wading through all the core-js history to see if the minified code matches up |
21:31 | <bradleymeck> | which at this point i'm just starting to assume it is all this feature detection |
21:32 | <bradleymeck> | i had to start labelling which kind of detection it is coming from, the only one i'm a bit scared of is the latest core-js i have to detect via the result of new species(...).constructor.name === 'FakePromise' |
21:33 | <bradleymeck> | but that gets minified out |
21:33 | <bradleymeck> | detecting species().constructor is typeof 'object' might be saner? |