00:26
<shu>
bradleymeck: Buffer seems like it might be a big issue here, or at least the Buffer polyfill
00:31
<bradleymeck>
I could believe it, but I also am skeptical of it's actual usage
00:31
<bradleymeck>
The only big API people use on it is slice from node side which is what it is emulating
00:34
<shu>
bradleymeck: slice is already overridden; i think usage of .map and .filter on Buffers should be rare
00:34
<shu>
bradleymeck: so that leaves .subarray, which is the same semantics as Buffer#slice in node at least
00:34
<shu>
any intuition on whether people use subarray over slice?
00:35
<bradleymeck>
mmmm, i think slice is the most common, but no clue it only takes 1 super popular package to skew results
00:36
<shu>
well, for node itself i guess it's "fine" so far as its buffer.js is updated in lockstep with V8 releases
00:36
<bradleymeck>
i think we could see usage % of that polyfill and then go back and do a more in depth check of what methods are being used of it
00:36
<shu>
oh? how do we see that?
00:37
<bradleymeck>
shu: we can check Node's gzemnid DB to scan the npm registry then do popularity queries
00:37
<bradleymeck>
but for old deploys it won't help
00:38
<bradleymeck>
i am more interested in a crawl of the pages with the buffer polyfill on w/e browser domain
00:38
<shu>
hm
00:38
<shu>
i am not good enough for http archive but it seems possible to craft some kind of query
00:38
<shu>
err, good enough to craft a query for http archive
00:39
<bradleymeck>
maybe MylesBorins has experience enough here if you can craft the hueristic to look for
00:39
<bradleymeck>
i know he has done some bigquery stuff before
02:43
<ljharb>
shu: the buffer polyfill is pulled in any time literally any npm module references `Buffer`
02:43
<ljharb>
tape, or deep-equal, or qs, i forget which, has to some kind of crazy hack to have an `isBuffer` impl without forcing the entire buffer polyfill into people's bundles
02:44
<shu>
indeed
02:44
<shu>
the question is whether they use the inherited methods that create new Uint8Arrays
02:44
<shu>
s/they/there is significant
03:08
<devsnek>
idk why one would map or filter a buffer
03:41
<shu>
yeah that's my hunch too, it comes down to subarray
03:44
<devsnek>
subarray is popular
03:44
<devsnek>
well idk if its popular in those projects
03:44
<devsnek>
but people use it to pass to textdecoders and stuff
03:44
<devsnek>
or textencoders
03:44
<devsnek>
one of those
04:36
<shu>
passing to TextDecoder would be fine as it’s just treating the subarrayed Buffer as a Uint8Array
04:36
<shu>
unless this is some Node-specific TextDecoder that takes Buffers
04:43
<devsnek>
no its standardized
16:17
<davepoole>
rkirsling! thanks, I wasn't sure what the expectation was. :)
19:40
<shu>
bradleymeck: ystartsev: littledan: fyi i wrote up a taxonomy of subclassing (thanks to domenic): https://github.com/syg/proposal-rm-builtin-subclassing#taxonomy-of-subclassing
19:41
<bradleymeck>
i'm still poking around on if there are any actual usages except feature detections before finalizing a detection script
19:48
<devsnek>
we need more proposals with `rm` in the name
19:49
<rkirsling>
I like the ㊟
19:50
<shu>
:)
19:50
<devsnek>
why is delegating to `this.constructor` not sufficient
19:51
<shu>
what does "sufficient" mean?
19:51
<devsnek>
said another way, why did @@species ever exist
19:51
<shu>
because MS tried to ship just delegating to `this.constructor` and it broke stuff
19:51
<bradleymeck>
devsnek: well I've uncovered all sorts of hellish stuff with false positives
19:51
<bradleymeck>
yea ^
19:51
<devsnek>
aw
19:51
<bradleymeck>
Array.prototype.map.call(window, String, 1) is a thing apparently
19:52
<bradleymeck>
IDK WHAT IT MEANS
19:52
<bradleymeck>
what are you doing
19:52
<shu>
to be clear: broke stuff that expected, pre ES6, to always return e.g. an Array
19:52
<bradleymeck>
[].slice.call(arguments) is also a big thing, same for NodeList
19:52
<bradleymeck>
lots of those
19:52
<devsnek>
ok and for the static promise methods
19:53
<devsnek>
i feel like people rely on those doing delegation to `this`
19:53
<shu>
those were added in ES6 and were given the delegation treatment out of consistency
19:53
<shu>
are there actual subclasses of Promise that depend on the delegation in the constructor methods?
19:54
<devsnek>
i'm fairly sure i've seen that
19:54
<devsnek>
but i'd have to double check
19:54
<shu>
i haven't really, citations would be good
19:55
<rkirsling>
wowsers, I'd finished reading through this before but I didn't realize about Type III
19:55
<bradleymeck>
extending Promise is super rare with a bunch of regexp checks against httparchive
19:56
<shu>
Type III and IV are the "delenda est" types but unfortunately Type II already incurs a big cost :(
19:56
<rkirsling>
yeah
19:56
<shu>
dan proposed an alternative in issue #1, would be interesting to think through if we can support Type II somehow without the burden
19:59
<Bakkot>
shu: there's another category, not yet relevant, which is whether built-in methods delegate to property lookups on their _arguments_, rather than looking into their slots directly (e.g. would (new Set).union(myFakeSet)` look up `Symbol.iterator` on `myFakeSet`)
19:59
<shu>
ah interesting
20:00
<bradleymeck>
is that really a species concern?
20:00
<bradleymeck>
or just a protocol concern
20:00
<Bakkot>
no, not really, but neither is type IV in the above taxonomy
20:01
<shu>
well, is it a *subclassing* concern
20:01
<Bakkot>
I have more feelings about this one because you can't work around it by overriding enough methods, which you can for all the other types
20:01
<shu>
the reason Type IV is included is because actions on builtin regexps are unreasonably difficult to optimize because there are hooks on via `this.exec` or whatever
20:02
<shu>
Bakkot: i can squint and look at your type and sweep it under the rug as "not harmful" in the way that RegExp[@@match] isn't so bad if you just look at it as a protocol that String uses, and not RegExp built-ins themselves use
20:03
<shu>
Bakkot: union(arg) accepting any iterable arg is reasonable and not too much burden for implementations
20:03
<shu>
conceptually you basically specced a union that takes Sets, and an overload that takes iterables
20:04
<shu>
you have a single decision point of going to the slow path that's easy-ish to stay on
20:05
<Bakkot>
shu: `difference` is maybe a more interesting case, because there you want to invoke `.has` on your argument, not `[Symbol.iterator]`
20:05
<shu>
ah
20:05
<shu>
yeah that's harder
20:05
<Bakkot>
still a protocol, just using a named property instead of a symbol one
20:06
<Bakkot>
(I guess a third way is to add `Symbol.has`)
20:07
<shu>
i think my preference here is to be to require explicit casts via Set()
20:07
<shu>
s/to be//
20:07
<ljharb>
i actually prefer constructor hooks, a la bradley's map proposal
20:07
<shu>
remind me what that is?
20:07
<ljharb>
then no methods need to be overridden ever, you'd just provide different hooks
20:07
<ljharb>
like `super(iterable, { toValue() {} })` etc
20:08
<shu>
hmm, seems scary
20:08
<ljharb>
for example, i could see Map and Set accepting a hook that lets you use something besides SameValueZero for comparison
20:08
<Bakkot>
shu: :(, that means you can't use a subclass without big-O penalties
20:09
<shu>
Bakkot: yeah, i can live with a protocol
20:09
<Bakkot>
ljharb historically "you can't add functionality on top of the built in things, you just have to trust the language designers to have added all the things you might need already" has not been a good philosophy, I think
20:09
<ljharb>
oh totally agree
20:09
<shu>
but might ask there be explicit in-spec fast paths then for passing actual Sets
20:09
<ljharb>
and i think you should be *able* to add new things
20:09
<ljharb>
but i also think that all the use cases of *overriding* existing things could be done with hooks
20:10
<Bakkot>
shu: I feel like engines could just do the actual property gets, confirm they're the built-in ones, and then go to the fast path
20:10
<Bakkot>
no?
20:10
<Bakkot>
if that's so I wouldn't think the spec would need to add the fast paths
20:10
<shu>
Bakkot: i was thinking the fast path would have no observable get of .has
20:11
<shu>
Bakkot: it'd consult the internal slots of the Set only
20:11
<Bakkot>
ljharb of course they could be done with _enough_ hooks, but, like, I don't think we should trust that we can add the things one might reasonably need
20:11
<Bakkot>
ljharb as another example I think it is perfectly reasonable to make a thing which is not a Set at all under the hood, but conforms to the interface, which could be passed as an argument to Set.prototype.union
20:12
<ljharb>
Bakkot: i totally agree that hooks would never be sufficient
20:12
<ljharb>
i don't agree with that last point tho
20:12
<ljharb>
things that accept arraylikes, versus things that accept arrays, are different things
20:12
<Bakkot>
shu that would make me sad but it wouldn't be the end of the world
20:12
<Bakkot>
ljharb Arrays are magic; they are not a good example to follow.
20:13
<ljharb>
ok, things that accept thenables are different than things that accept promises
20:13
<ljharb>
or toStringables vs strings, etc
20:13
<shu>
Bakkot: isn't this the same reason RegExp subclassing is bad now? maybe difference reasonly only has one such override point with `.has`, but it's a death by thousand cuts kind of thing
20:13
<shu>
hm something went wrong with the input field there
20:13
<ljharb>
Bakkot: at any rate it's also kind of that ^ that it's fine if "the interface" is one method, but not fine if it's N
20:15
<Bakkot>
shu I think the tradeoffs are different, basically. I don't think there's that much utility in making a fake regexp, and there people care about absolutely maximum speed, whereas there is a lot of utility in making a Set-like, and the speed is not quite so much of a concern
20:16
<Bakkot>
ljharb I basically do not understand what analogy you are trying to make
20:16
<ljharb>
hm, ok
20:16
<shu>
Bakkot: crucially, a lot of utility in making a Set-like by actually subclassing Set and overriding e.g. has
20:16
<shu>
not a completely custom Set-like
20:16
<ljharb>
(there's also utility in doing that and not having `Set.prototype.has.call` provide different behavior)
20:17
<Bakkot>
shu two things: 1.) if you do that, then the fast-path has to use the userland has, and 2.) I don't agree actually; I think the "wraps a Set" vs "extends Set" distinction is basically an implementation detail and either approach is reasonable
20:17
<Bakkot>
ljharb I don't think users should think about Set.prototype.has.call basically ever
20:18
<Bakkot>
shu s/the fast path has to use the userland has/there cannot be a fast-path which avoids observable .has lookup/
20:20
<shu>
Bakkot: hm, i'd have to think more about the "speed is not quite so much of a concern"
20:20
<shu>
ofc none of this is an issue if speed is not a concern
20:20
<shu>
well, not quite true
20:20
<shu>
but this would all be decidedly less concerning, at least
20:23
<shu>
Bakkot: reading through the current Set code i remain leaning towards another way to provide custom 'has' functionality than overriding the method
20:23
<shu>
Bakkot: one can reasonably expect Set#delete or Set#add to invoke Set#has, but it does not
20:24
<Bakkot>
> one can reasonably expect Set#delete or Set#add to invoke Set#has
20:24
<Bakkot>
... can one?
20:24
<Bakkot>
by contrast, the set constructor itself does invoke Set#add
20:24
<shu>
that is certainly a good point
20:24
<Bakkot>
(which falls under your type 4 taxonomy)
20:25
<Bakkot>
shu: but also, as I say I am more concerned with invoking these methods on _arguments_ than on `this`; for `this` you can always get around it by overriding all the methods
20:27
<shu>
i can certainly see the argument that different data structures have different expectations of being subclassable piecemeal
21:26
<rkirsling>
shu: btw you mentioned trying to deal with a single method as an experiment, but I think for us it's ArraySpeciesCreate as a whole that results in greatest implementation complexity
21:27
<shu>
rkirsling: yeah, good point, should've been more precise in what i meant
21:27
<rkirsling>
(mind you I'm not used to digging into this particular area so there could be more stuff that's just not jumping out as obviously, but most of the rest just seems to be reflecting the spec itself, whereas our DFG has watchpoints for array species)
21:27
<shu>
rkirsling: it'd be good to look at the diff of an implementation of a single builtin under two scenarios: both types II and III removed, and only type III removed
21:28
<rkirsling>
yeah that's fair
21:28
<shu>
rkirsling: for V8 at least, the stuff that looks out for species looks out for more than species, despite the name, which might be a gotcha
21:28
<rkirsling>
ahh yeah, could be
23:07
<rkirsling>
TabAtkins: wow, that's the most positive subdiscussion I've seen in the pipeline repo in recent memory
23:08
<rkirsling>
#yay
23:10
<TabAtkins>
yeah it's weird how heated people get
23:13
<Bakkot>
rkirsling which?
23:14
<rkirsling>
last two comments at https://github.com/tc39/proposal-pipeline-operator/issues/167
23:15
<rkirsling>
makes me kinda excited
23:15
<rkirsling>
let's do this
23:16
<ljharb>
TabAtkins: is the style you prefer the `x => await => y` form where x and y are functions? or is it a different one
23:16
<TabAtkins>
That's F#-style, so no
23:17
<ljharb>
hm, ok
23:17
<TabAtkins>
I'm going to be advocating for plain-jane Hack-style, where the RHS is just "any expression", and there's a binding for the # variable over it (bound to the LHS).
23:17
<ljharb>
littledan's frameworks outreach call today seemed to prefer that one iirc
23:17
<rkirsling>
that's what point (6) is about
23:17
<TabAtkins>
littledan is going to be advocating for that, yeah
23:17
<ljharb>
so you'd do `await #` if that's the semantic you wanted?
23:18
<TabAtkins>
Put the await wherever you need it, yeah.
23:18
<ljharb>
(i don't like # as the placeholder choice there but obv that's a stage 2 debate)
23:18
<TabAtkins>
(Rather than being forced to put it by itself on a pipeline step)
23:18
<ljharb>
right
23:18
<TabAtkins>
`val |> async(#) |> foo(await #)` if that's the clearest way to express your intent, for instance
23:18
<ljharb>
and for those who prefer the F# style, you could immediately invoke "the function you used in F#" to use it in "hack-style"?
23:19
<TabAtkins>
`val |> asyncFn(#) |> foo(await #)`, rather
23:19
<TabAtkins>
Yeah, you just call it like you would in normal JS.
23:19
<TabAtkins>
If normal JS would have you do `let x = foo(1,2)(3)`, the hack-style pipeline does `3 |> foo(1,2)(#)`
23:19
<rkirsling>
`|> await` is certainly the part that makes me frown the most about the other approach
23:20
<rkirsling>
your point (7) is strangely compellingly worded for being as obvious as it is though
23:20
<ljharb>
TabAtkins: and expressions are lazily or eagerly evaluated?
23:20
<Bakkot>
TabAtkins if you present on this, can you contrast to just writing `$ = val; $ = asyncFn($); $=foo(await $)`?
23:20
<TabAtkins>
rkirsling: It's apparently not obvious! I've polished it over time talking with other people!
23:20
<rkirsling>
:)
23:20
<rkirsling>
good work
23:21
<TabAtkins>
ljharb: eagerly, it's *exactly* as if you'd taken the LHS and put it in the RHS in place of the #
23:21
<TabAtkins>
(module the fact that the LHS is only executed once even if you use # multiple times)
23:21
<Bakkot>
TabAtkins wait that would be very surprising to me
23:21
<Bakkot>
lazily seems the obvious thing
23:21
<ljharb>
yeah that surprises me too
23:21
<TabAtkins>
Bakkot: Basically that exact example is the main *competitor* to pipeline ^_^
23:21
<TabAtkins>
wait now i'm surprised
23:21
<devsnek>
wait what does lazy mean in this context
23:21
<TabAtkins>
that's the same in every pipeline
23:21
<Bakkot>
if I don't hit a step in the pipeline, I don't expect to see side effects from the hitting that step
23:22
<ljharb>
`console.log(1) |> await # |> console.log(2)`
23:22
<TabAtkins>
OH
23:22
<ljharb>
i would expect 2 not to log until after the tick
23:22
<TabAtkins>
ok yeah
23:22
<rkirsling>
yeah I think there was a misspeak?
23:22
<ljharb>
that's lazily
23:22
<ljharb>
(i believe)
23:22
<TabAtkins>
Yeah, LHS is executed, *then* pipeline body is.
23:23
<TabAtkins>
You literally can't execute the RHS until you're done with the LHS.
23:23
<ljharb>
right
23:23
<Bakkot>
TabAtkins re "that exact example is the main *competitor* to pipeline" - right, so, contrasting to the main competitor seems like it would be valuable in your presentation
23:23
<devsnek>
its like normal evaluation
23:23
<ljharb>
presumably they're parsed eagerly but evaluated lazily
23:23
<ljharb>
right
23:23
<devsnek>
except with a different delimiter
23:24
<devsnek>
i'd be interested to see some codebases where pipelining comes in handy
23:24
<TabAtkins>
Bakkot: Yup, explaining the benefit of pipeline over "just write JS without pipeline" is of course a necessary piece of this ^_^
23:24
<devsnek>
i don't think i've ever had something that felt ungangly and in need of a pipeline
23:24
<devsnek>
ungainly*
23:25
<Bakkot>
TabAtkins well, it's more dramatic for Hack style than F# style, since in F# style you are composing functions directly, whereas in Hack style you're just having a binding created for you - that is, Hack style is much closer to that sample than F# style is
23:25
<TabAtkins>
The benefit actually ends up *relatively* small; it's not a world-changer like arrow-function syntax. But it does let you write some common code patterns in a cleaner way, and importantly, lets you get the benefit of doing complex manipulations of a value via multiple statements, but in an expression context.
23:25
<rkirsling>
I think anywhere you have 2+ nested calls it could be potentially nicer
23:25
<ljharb>
devsnek: react HOCs is a big one
23:25
<ljharb>
devsnek: currently it's `withA(withB(withC(withD(Component))))`
23:26
<rkirsling>
yuck
23:26
<Bakkot>
"lets you get the benefit of doing complex manipulations of a value via multiple statements": mm, sometimes, but only if you don't need intermediate values multiple times; do expressions seem like a much more general solution to that problem
23:26
<devsnek>
like trying to extend multiple things?
23:26
<ljharb>
devsnek: `Component |> withD |> withC |> withB |> withA` is much clearer
23:26
<ljharb>
devsnek: it's not extending, it's composing/wrapping, but yes
23:26
<ljharb>
HOC is a "higher-order component"
23:26
<devsnek>
seems like a weird pattern
23:27
<TabAtkins>
Bakkot: Yup, cut the gordian knot of do exprs and I'd be okay with dropping pipelines ^_^
23:27
<ljharb>
devsnek: it's also how every single usage of redux in the react world worked, prior to hooks. it's a common pattern.
23:27
<devsnek>
i've never really done any frontend stuff
23:28
<devsnek>
because i hear that patterns like HOC are a thing
23:28
<TabAtkins>
lessee, from the HOC documentation:
23:28
<TabAtkins>
https://reactjs.org/docs/higher-order-components.html
23:28
<devsnek>
i really want to move do expressions forward
23:28
<TabAtkins>
const CommentListWithSubscription = withSubscription(
23:28
<TabAtkins>
CommentList,
23:28
<TabAtkins>
(DataSource) => DataSource.getComments()
23:28
<TabAtkins>
);
23:28
<rkirsling>
> the HOC doc
23:28
<rkirsling>
FTFY
23:29
<rkirsling>
devsnek: just `do` it
23:29
<TabAtkins>
would be `const CommentListWithSubscription = CommentList |> withSubscription(#, DataSource=>DataSource.getComments());`
23:29
<devsnek>
but people think that control flow inside a block that is part of a do expression is not good
23:29
<Bakkot>
devsnek some people (like me) think that, but other people disagree
23:29
<TabAtkins>
I can *definitely* see how confusing that would be with another withX() wrapper or two
23:29
<Bakkot>
devsnek completion values are also a sticking point
23:30
<Bakkot>
devsnek though, it occurs to me you could just require the last statement in the `do` be an expression, so it'd be obvious...
23:30
<devsnek>
iirc completion values were less of a blocker and more of a "fine assuming we make sure completions in the spec are clean"
23:30
<Bakkot>
devsnek ehhh: https://github.com/tc39/proposal-do-expressions/issues/21
23:31
<devsnek>
i wouldn't like to force that
23:31
<ljharb>
devsnek: i mean, it'd be useful in express middlewares too, i'd imagine
23:31
<devsnek>
isn't express middleware flat
23:31
<devsnek>
`app.use(x); app.use(y)`
23:31
<ljharb>
atm yes
23:31
<TabAtkins>
Bakkot: Forcing that means I don't get the nice clean "turn if() into an expression by wrapping it in a do{}" functionality :(
23:31
<ljharb>
(ftr i also think control flow in do expression blocks is bad)
23:32
<devsnek>
TabAtkins: yeah i want that too lol
23:32
<Bakkot>
TabAtkins hm, true
23:32
<devsnek>
in any case my argument was that people use repls
23:32
<devsnek>
which have the same semantics as do expressions
23:33
<devsnek>
and aside from block/object parsing, they seem ok with how those work
23:33
<devsnek>
people also use completion of scripts with the node vm module
23:33
<Bakkot>
people are OK with repls because they don't care what the completion value of a declaration or a loop is
23:34
<devsnek>
but it tells them what it is
23:34
<ljharb>
^ that
23:34
<ljharb>
sure
23:34
<devsnek>
and they don't go on twitter raving about it
23:34
<ljharb>
but that doesn't mean they pay attention to it
23:34
<Bakkot>
devsnek yeah but that doesn't mean they know
23:34
<devsnek>
afaik
23:34
<ljharb>
lol the completion value of `console.log` is undefined, and that actually *does* confuse a ton of people, they ask on irc a lot
23:34
<TabAtkins>
I will say that I've written plenty of for loops in my repl and never once realized consciously that it even has a value
23:35
<devsnek>
github puts an enormous green checkmark on the highest rated comment now
23:35
<devsnek>
like it solves the issue or smth
23:35
<Bakkot>
alternative possibility: the last statement cannot be a loop or declaration
23:35
<ljharb>
devsnek: huh, link?
23:35
<devsnek>
not being a declaration makes sense
23:35
<rkirsling>
devsnek: highest _rated_?
23:35
<Bakkot>
defining "last" is a little weird though
23:35
<devsnek>
https://github.com/tc39/proposal-do-expressions/issues/21#issuecomment-359160212
23:35
<devsnek>
but i would use loops too
23:36
<Bakkot>
I do not see a giant checkmark
23:36
<rkirsling>
nor do I
23:36
<Bakkot>
devsnek ... would you? why?
23:36
<ljharb>
devsnek: i do not see that. github chrome extension?
23:36
<ljharb>
devsnek: maybe you're using "refined github"
23:36
<devsnek>
i am using refined github
23:36
<Bakkot>
why would anyone ever want the completion value of a loop?
23:36
<devsnek>
it could be that i guess
23:36
<ljharb>
it does a bunch of weird things, that must be one of them
23:36
<devsnek>
Bakkot: because they can have values
23:36
<devsnek>
and i want to use that
23:36
<Bakkot>
devsnek specifically when?
23:36
<Bakkot>
when do you want to use that?
23:36
<devsnek>
idk
23:37
<Bakkot>
:|
23:37
<devsnek>
sometimes you have to factor a loop out
23:37
<devsnek>
to use `return` from the body
23:37
<Bakkot>
fwiw a _lot_ of people get confused by loops in React not making an array
23:37
<devsnek>
or assign to a variable
23:37
<Bakkot>
like, endlessly they are confused by this
23:37
<TabAtkins>
Bakkot: completion value of a loop is *basically* the return value of a .reduce()
23:37
<devsnek>
"loops in react not making an array" what?
23:37
<Bakkot>
s/in React/in JSX/
23:37
<devsnek>
what does a loop in jsx mean
23:37
<Bakkot>
https://stackoverflow.com/questions/22876978/loop-inside-react-jsx
23:38
<devsnek>
that's just people not understanding the basics of imperative programming
23:38
<TabAtkins>
That said, if I'm reducing like that, I already need to declare a var to hold the intermediate results, so I might as well just list that var's name *after* the loo pto get it returned
23:39
<rkirsling>
that seems pretty JSX-specific, in that it's conflating things that wouldn't be conflated in a templating language
23:39
<TabAtkins>
In `let sum = do{let sum = 0; for(const x of vals) sum += x; sum};`, the `; sum;` at the end isn't killing me
23:39
<devsnek>
anyway if we make sure completions in the language are clean
23:39
<Bakkot>
I think a lot of people will be surprised that `do { for (i = 0; i < 10; ++i) i }` does not give you an array with [0, ..., 9]
23:39
<devsnek>
and people generally don't do weird stuff
23:39
<devsnek>
i don't think it will be a problem
23:40
<devsnek>
like there's always that one person who writes jsfuck
23:40
<TabAtkins>
Looking back at the hoc docs, they give an example of using multiple hocs at once as:
23:40
<Bakkot>
https://github.com/tc39/proposal-do-expressions/issues/14 is the issue for loops in particular
23:40
<TabAtkins>
`withRouter(connect(commentSelector)(WrappedComponent))`
23:40
<devsnek>
Bakkot: let them be surprised, they might learn how imperative programming works
23:40
<TabAtkins>
and already I'm having trouble reading that.
23:40
<Bakkot>
devsnek :(
23:41
<Bakkot>
devsnek that seems like... not a good design principle
23:41
<TabAtkins>
`WrappedComponent |> connect(commentSelector)(#) |> withRoute(#)`
23:41
<devsnek>
i mean
23:41
<devsnek>
i don't think do expressions exacerbate that misunderstanding
23:41
<Bakkot>
devsnek: specifically, there is nothing about "imperative programming" which means that has to return a single value and not an array
23:41
<rkirsling>
Bakkot: I do disagree with the OP there though
23:41
<Bakkot>
we could define do expressions in a way which makes that return an array
23:41
<ljharb>
it still doesn't make any sense to me that a loop has a completion value.
23:41
<devsnek>
you're doing something and not putting the value anywhere
23:42
<Bakkot>
devsnek I mean that is true for any do expression
23:42
<Bakkot>
the whole point of using completion values is that you are not putting the value anywhere
23:42
<devsnek>
like i get that its confusing for n=1
23:42
<Bakkot>
it just gets picked up for you
23:42
<rkirsling>
as TabAtkins said, why wouldn't you expect it to be the reduce result? why would you expect it to build you a thing implicitly?
23:42
<ljharb>
devsnek: why would you have a loop with n < 2
23:42
<ljharb>
devsnek: actually i find it confusing with n > 1
23:43
<devsnek>
ljharb: i get that people are surprised about completions
23:43
<devsnek>
but if you evaluate it x times
23:43
<devsnek>
where x is not one
23:43
<rkirsling>
like, if there is no array, where would it come from
23:43
<ljharb>
rkirsling: the array thing i don't find reasonable
23:43
<devsnek>
i don't understand the logic of where all those values are doing
23:43
<devsnek>
going*
23:43
<ljharb>
(altho the for..of part kind of makes sense to me)
23:43
<devsnek>
to me it feels like someone copy pasted some code without understanding it
23:44
<ljharb>
isn't that stackoverflow's growth model
23:44
<devsnek>
it feels like the kind of question we get in discord.js support server
23:44
<Bakkot>
I don't think that "the completion value of a loop is an array holding all of the completion values of each step of the loop" is a totally unreasonable thing to think
23:44
<Bakkot>
it isn't, but like, there is no particular reason for it not to be
23:45
<devsnek>
yeah but at that point you have some idea of completion values
23:45
<Bakkot>
... yes?
23:45
<Bakkot>
and?
23:45
<TabAtkins>
diy list comprehension, yo
23:45
<rkirsling>
insofar as users have no conception of completion values, sure lol
23:45
<devsnek>
the jsx thing has nothing to do with completions
23:45
<Bakkot>
ok pretend I never brought up JSX
23:45
<devsnek>
i'm down to talk about making the loop produce an array
23:45
<Bakkot>
ehh
23:45
<Bakkot>
I don't really think it should
23:45
<devsnek>
but i don't think anything here is beyond comprehension
23:46
<devsnek>
i also don't think it should
23:46
<rkirsling>
^ pun intended?
23:46
<devsnek>
lol ross
23:46
<Bakkot>
my actual position is that we should not expose to users the completion value of a loop
23:46
<rkirsling>
I will concede that https://github.com/tc39/proposal-do-expressions/issues/14#issuecomment-359529937 gives me pause
23:46
<ljharb>
^ +1
23:46
<rkirsling>
yeah I think I can agree with that sentence too
23:46
<devsnek>
ok to rephrase
23:46
<devsnek>
i'm ok talking about what the completion value of a loop should be
23:46
<devsnek>
but i don't think a loop having a completion value is inherently a bad thing
23:47
<Bakkot>
right, and my position is the negation of that last sentence
23:47
<devsnek>
whether its undefined or an array or the last expression or whatever
23:47
<TabAtkins>
I am cool with all the non-obvious statement cases being defined as producing undefined. So long as if() and try/catch give me their final values, everything else can take a hike as far as i'm concerned
23:48
<ljharb>
TabAtkins: the if block?
23:48
<TabAtkins>
yeah
23:48
<ljharb>
TabAtkins: blocks having a completion value of their last statement's makes sense to me
23:48
<ljharb>
any blocks
23:48
<ljharb>
but not loop bodies
23:48
<Bakkot>
my position is, I would like us to only allow do expressions for which the completion value is going to be obvious - so, you should not be able to end a do expression in a loop (or a declaration, which is also weird)
23:48
<devsnek>
i agree with tabatkins
23:48
<ljharb>
sounds like we have a compromise point
23:48
<devsnek>
that being
23:48
<devsnek>
the loop is allowed to be the last item
23:48
<devsnek>
but it just always gives undefined
23:49
<ljharb>
oh. i don't think that was what tab said
23:49
<rkirsling>
wait whoa why is `do {} while (false)` different from `{}`
23:49
<TabAtkins>
(I'm also fine with "everything works exactly as if you just plugged it into eval()", fwiw.)
23:49
<ljharb>
rkirsling: because it's a loop and `{}` is not
23:49
<TabAtkins>
devsnek accurately summarized me, yeah
23:49
<rkirsling>
yes
23:49
<rkirsling>
that is my question
23:49
<Bakkot>
TabAtkins would you also be ok with, ending a do expression with a loop is a syntax error?
23:49
<devsnek>
that's part of the es2015 completion reform
23:49
<TabAtkins>
yeah
23:49
<devsnek>
Bakkot: i would be against that
23:49
<Bakkot>
rkirsling basically "things are sometimes empty, sometimes not, at runtime" was held to be confusing
23:49
<ljharb>
rkirsling: i'm saying that's why it's different. loops make 0, 1, or N completion values, blocks make 1. there's no ambiguity about blocks, there is with loops
23:49
<devsnek>
idk how strongly exactly
23:49
<Bakkot>
devsnek why?
23:50
<rkirsling>
ah
23:50
<rkirsling>
right, you might not even enter a loop body
23:50
<rkirsling>
that's a good point
23:50
<devsnek>
it feels wrong to limit things like that
23:51
<devsnek>
maybe i can be convinced
23:51
<devsnek>
i can't be convinced about control flow though, that's a must
23:51
<Bakkot>
devsnek fwiw this is the sort of limitation we could relax later
23:51
<Bakkot>
devsnek :(
23:51
<rkirsling>
it feels wrong but it's not unthinkable
23:51
<Bakkot>
devsnek control flow is also a thing which could be relaxed later
23:51
<devsnek>
nah my use case doesn't work without it
23:51
<Bakkot>
well
23:51
<Bakkot>
there are other use cases
23:51
<rkirsling>
loops would be puntable where non-loop block-based constructs aren't
23:51
<Bakkot>
sometimes we add things which do not meet your particular use case
23:51
<Bakkot>
that is ok
23:51
<devsnek>
i mean my motivation for possibly furthering the proposal
23:52
<Bakkot>
ah, fair
23:52
<devsnek>
is to allow control flow in expression positions
23:52
<Bakkot>
yeah
23:52
<ljharb>
that's something i feel pretty strongly shouldn't be allowed
23:52
<TabAtkins>
Given that the point of a do-expr is to *return a value*, I don't see a signfiicant difference between "ending with a loop returns undefined" and "ending with a loop is a syntax error".
23:52
<devsnek>
TabAtkins: yeah i see both sides
23:52
<rkirsling>
^ I like this phrasing
23:52
<Bakkot>
ok I might try to present the minimal form of do expressions
23:52
<ljharb>
TabAtkins: it's also to have statements in expression position
23:52
<devsnek>
i just don't like limitations
23:52
<TabAtkins>
The latter will catch programming mistakes earlier; the former will allow me to spin a loop for side effects at expression context.
23:52
<ljharb>
TabAtkins: which doesn't require a value
23:52
<Bakkot>
which basically ban anything confusing or contentious
23:53
<devsnek>
i find your banning of contentious items contentious
23:53
<ljharb>
lol
23:53
<Bakkot>
ljharb what expression positions don't require a value?
23:53
<ljharb>
Bakkot: i mean, there must *be* a value - like undefined - but it doesn't have to have meaning
23:53
<TabAtkins>
yeah the whole point of expression context is "here comes a value, beep beep"
23:53
<Bakkot>
devsnek sure, but the alternative appears to be "do expressions stagnate forever", so...
23:53
<rkirsling>
combining "make everything an expression" and "for side effects" just hurt my head
23:53
<ljharb>
`void do { … }` is a totally fine way to run side effects in expression positions.
23:53
<rkirsling>
(or maybe it was my heart)
23:53
<ljharb>
where "fine" is that dog meme
23:53
<devsnek>
yeah like
23:54
<Bakkot>
ljharb I mean to say, when would that come up? I feel like "I am forced to be in expression position, but I don't care what the value is" is fairly rare
23:54
<devsnek>
its weird
23:54
<ljharb>
Bakkot: true
23:54
<devsnek>
but that's what i like about js
23:54
<ljharb>
Bakkot: but i think ending with a loop, and not a value, is equally rare
23:54
<ljharb>
Bakkot: or will be in an expression position, i mean
23:54
<Bakkot>
ljharb well, I think some people will expect it to get an array, is the thing
23:54
<Bakkot>
and try it, and get bit
23:54
<ljharb>
fair
23:55
<rkirsling>
I mean, I would say it's on par with the early error for **
23:55
<devsnek>
what if the completion value of a loop is a string containing info for the mailing list
23:55
<ljharb>
… maybe still better than "last value"
23:55
<ljharb>
:-p
23:56
<rkirsling>
"congrats you're the 10,000th caller! what do you think this should do"
23:56
<TabAtkins>
"side effects in expression context, don't care about the value" can still be done with `do{ for(){...}; 0}`
23:56
<devsnek>
lol
23:56
<devsnek>
my main thing is thinking about how generated code can use do expressions
23:56
<devsnek>
that's my primary use case for them anyway
23:56
<ljharb>
TabAtkins: very true.
23:57
<ljharb>
devsnek: generated code can also generate boilerplate to capture whatever return value you want
23:57
<devsnek>
yeah but that's extraordinarily difficult
23:57
<rkirsling>
what sort of generated code
23:57
<Bakkot>
generated code has a lot of freedom to just do things in a different way
23:57
<devsnek>
like babel codemods
23:57
<Bakkot>
put an IIFE there, rewrite control flow, whatever
23:57
<devsnek>
i don't mean compiler output
23:58
<ljharb>
devsnek: how is it difficult? add `let completion;` to the top, and `completion =` in front of the last value in the loop body, and `completion;` after?
23:58
<Bakkot>
TabAtkins re the "reduce" thing, one other case is, you are searching for an item and want that item
23:58
<Bakkot>
in which case it's more awkward
23:58
<devsnek>
ljharb: i was talking more about control flow
23:58
<Bakkot>
this is the example in https://github.com/tc39/proposal-do-expressions/issues/34
23:58
<Bakkot>
under "for loop"
23:58
<devsnek>
rewiring the values is pretty easy
23:59
<Bakkot>
rewiring the control flow is also pretty easy
23:59
<TabAtkins>
ah yeah, without a `break with` that rewrites the completion value, you gotta do the "declare a temp, for(), temp" thing
23:59
<devsnek>
yeah just fork regenerator
23:59
<Bakkot>
generators are way more powerful than the break-continue-return kind of control flow we're talking about here