01:05
<Chris de Almeida>
please add yourself to the attendees list at the top of today's notes doc. (and yesterday's if you missed). thank you 🙏
01:27
<Chris de Almeida>
I captured the queue
01:30
<rkirsling>
7 years, wow
01:39
<Ashley Claymore>
Fun co-incidence. The 'toSorted' spec bug was caught by a Japanese developer
01:41
<Jack Works>
"Summing a list is a very common operation and is one of the few remaining use cases for Array.prototype.reduce."
01:41
<Jack Works>
are we going to discouraging use of [].reduce?
01:43
<rkirsling>
no, because you don't necessarily want the new behavior
01:43
<jkup>
The reduce will be faster still, right?
01:44
<Michael Ficarra>
@jkup an equivalent loop will, at least
01:44
<Michael Ficarra>
but you REALLY don't want to do that
01:44
<Michael Ficarra>
your result can accumulate a ton of error
01:44
<Jack Works>
the wording sounds like reduce is a bad thing (actually yes in most cases it reduce the code readability)
01:45
<Jack Works>
but I'm not expecting that we should have a proposal to solve "the few remaining use cases of reduce", other motivations are ok thou
01:46
<rkirsling>
but you REALLY don't want to do that
I mean, in a specific case you might know that you're just adding e.g. a couple dozen single-digit ints or something though
01:46
<rkirsling>
just depends on the guarantees you're working with
01:47
<shu>
did you know reduce is pronounced re-doo-che
01:47
<Michael Ficarra>
@rkirsling if the number of values you're summing is small enough, the difference in perf between compensated and uncompensated is negligible anyway
01:47
<rkirsling>
touchĂŠ!
01:47
<Michael Ficarra>
and once it gets large enough to care about perf, you should also care about error accumulation
01:47
<Jack Works>
did you know reduce is pronounced re-doo-che
not re-diu-s, or it's a ghoti joke?
01:48
<rkirsling>
it is neither, it's just a "let's pronounce this word in a silly way" joke
01:49
<rkirsling>
...the silly way in this case being like Italian, presumably :D
01:53
<Chris de Almeida>
the contents of the schedule may have shifted in flight. presenters please review and let us know if any issues
01:54
<bakkot>
The reduce will be faster still, right?
I would guess this will actually be faster because the reduce has to do callbacks. at least in the case where the engine knows it's a list of numbers already, sumPrecise can avoid a lot of work, which may make up for the difference
01:58
<hax (HE Shi-Jun)>
I feel setdefault is ok. Not sure why people prefer callback...
01:58
<ljharb>
because sometimes the default is expensive to compute.
01:58
<Michael Ficarra>
the callback can be lifted outside a loop, I'm not too worried about it
01:58
<Jack Works>
getOrSet
01:58
<bakkot>
it's true that people can avoid re-creating the callback, but they're not going to
01:58
<shu>
yeah
01:59
<ljharb>
i am also skeptical that prepending () => would have a noticeable perf hit, but have no way to argue that
01:59
<bakkot>
allocating throwaway closures is a lot more expensive than 0
01:59
<shu>
it will have a noticeable perf hit in a hot loop for sure?
01:59
<shu>
allocation + call
01:59
<rbuckton>
allocating throwaway closures is a lot more expensive than 0
function returnZero() { return 0; }
01:59
<shu>
yeah people aren't gonna do that, i agree with kevin there
01:59
<shu>
people will write ()=>0
01:59
<bakkot>
100% of the time
01:59
<Michael Ficarra>
@shu @bakkot when it's [], you're creating tons of thrown-away arrays instead of just not calling a callback
02:00
<shu>
when what is []?
02:00
<bakkot>
but at least you don't have to call anything
02:00
<ljharb>
in Iterator.concat the throwaway arrays were supposed to be nbd
02:00
<bakkot>
when what is []?
when that's the default value
02:00
<Michael Ficarra>
people who care about perf already know to lift regexps outside of loops for example, this is the same thing
02:00
<hax (HE Shi-Jun)>
because sometimes the default is expensive to compute.
But current map.set has similar issue?
02:00
<shu>
if the default value is [], presumably you want a different empty array?
02:00
<ljharb>
But current map.set has similar issue?
currently you'd have to do your own has/get/set, and the set would be conditionally evaluated, so it'd always be the same as a callback (or better)
02:01
<Michael Ficarra>
in Iterator.concat the throwaway arrays were supposed to be nbd
we're talking about vastly different numbers of throwaway arrays here
02:01
<hax (HE Shi-Jun)>
if the default value is [], presumably you want a different empty array?
map.setdefault(key, []) already give u a diff empty array?
02:01
<shu>
yes
02:01
<shu>
that's why i don't understand michael's point
02:01
<Eli Grey>
insert should be the value and use a getter for the function use case in emplace imo
02:01
<shu>
you can't have a single [] be the default
02:01
<shu>
unless you have copy-on-write []
02:01
<Michael Ficarra>
@shu I'm talking about doing this operation in a loop
02:02
<shu>
how can you lift out a default [] out of a loop?
02:02
<shu>
you want all the default empty arrays to be the literal same array object, which can be mutated?
02:02
<Michael Ficarra>
@shu you can't, that's the point, you make it a callback and lift that out
02:02
<shu>
wat
02:02
<shu>
why would you make a callback at all
02:02
<shu>
setdefault(k, []) does the right thing?
02:03
<Michael Ficarra>
@shu it would be more performant
02:03
<shu>
what
02:03
<Jack Works>

(joke) I guess we have to materialize ParseNode.

.emplace(key, complexExpression)
function emplace(key, value) {
    if (!this.has(key)) this.set(key, eval value);
    return this.get(key)
}
02:03
<shu>
are you saying doing a call on a const function that returns [] is faster than directly passing in []?
02:03
<Michael Ficarra>
a shared callback that is almost never called is more performant than tons of empty arrays that end up getting GC'd
02:04
<shu>
oh, i see, i missed the assumption that it's rarely a hit
02:04
<shu>
or, rarely a miss
02:04
<shu>
that depends on whether your loop mostly hits or mostly misses, right?
02:04
<ljharb>
that's the benefit of using a callback in the API as well; that the default fn would almost never be called, for when it's expensive
02:04
<Michael Ficarra>
correct @shu
02:05
<shu>
but a callback version would be more expensive when it is mostly misses
02:05
<shu>
so it 'just depends'
02:05
<Jack Works>
well, what react do is good, but bad
02:05
<Jack Works>
they call it when it is a function
02:05
<Anthony Bullard>
Is it not possible to do both?
02:05
<Michael Ficarra>
@ljharb they're worried that people will write the callback inline so instead we'll be creating tons of fresh functions that need to get GC'd
02:05
<Jack Works>
Is it not possible to do both?
like react do to useState
02:05
<bakkot>
both is good
02:05
<Jack Works>
most cases it's good, some cases it's footgun
02:05
<shu>
yeah i'm fine with both
02:05
<Anthony Bullard>
I can’t attend tonight, just following chat
02:05
<Michael Ficarra>
I'm fine with both
02:06
<ljharb>
both is fine too
02:06
<Anthony Bullard>
I guess I should say allow either
02:06
<shu>
computeIfAbsentViaThisCallbackHere
02:06
<ljharb>
i've called it getOrSetIfAbsent before but that name is horrible
02:06
<Michael Ficarra>
is that in Java™️ 23?
02:06
<bakkot>
I like getOrInit or getOrInsert
02:06
<shu>
maybe getOrSetIfAbsentOrPresent
02:07
<bakkot>
the callback one is computeIfAbsent in java, which... works...
02:07
<Jack Works>
getWhenHasOrSetFromCallbackAndFinallyGetAgain
02:07
<Anthony Bullard>
I just meant the arg can be either a value or a function to return the value
02:07
<Anthony Bullard>
Obviously an issue if the value you want is a function
02:08
<Jack Works>
I just meant the arg can be either a value or a function to return the value
yes, react do this and sometimes it's a footgun
02:08
<bakkot>
oh, absolutely not that
02:08
<bakkot>
functions are valid values to store in a map
02:08
<hax (HE Shi-Jun)>
java have both:putIfAbsent ( = python setdefault), computeIfAbsent is callback version.
02:08
<Anthony Bullard>
But yeah emplace and emplaceWith
02:08
<Anthony Bullard>
Is better
02:08
<Jack Works>
functions are valid values to store in a map
hIGh oRDeR functions obviously
02:08
<Michael Ficarra>
I just meant the arg can be either a value or a function to return the value
WHY DO PEOPLE KEEP DOING THIS TO THEMSELVES?!
02:09
<Anthony Bullard>
WHY DO PEOPLE KEEP DOING THIS TO THEMSELVES?!
Yeah I thought of this after the fact
02:09
<Jack Works>
when you're in react and useState value might be a function, you do useState(() => fn)
02:09
<Anthony Bullard>
But other languages aren’t afraid of having separate methods/functions for value args and function args
02:10
<Jack Works>
how do you like extractors
02:10
<bakkot>
java have both:putIfAbsent ( = python setdefault), computeIfAbsent is callback version.
I don't like putIfAbsent because it doesn't suggest that it gives you the current value in the case that it's present
02:10
<bakkot>
computeIfAbsent doesn't really either but I guess it's more obviously going to return something because "compute" suggests returning something
02:10
<Anthony Bullard>
Go for instance has many doSomething(arg1, value) doSomethingFunc(arg1, fn) pairs
02:11
<ljharb>
in ruby i think you'd have it take a block vs take a function, but most langs don't have blocks like that iirc
02:11
<Anthony Bullard>
The naming doesn’t align obviously with ES style, but the pairing is fine and easy to grok
02:12
<Jack Works>
well naming is that hard, let's call it Map.prototype['007ae09e-b0e5-4939-b6f0-210e46f70538']
02:12
<Eli Grey>
getters are good for this
02:12
<Anthony Bullard>
setDefault and setDefaultWith
02:13
<bakkot>
well naming is that hard, let's call it Map.prototype['007ae09e-b0e5-4939-b6f0-210e46f70538']
advantage of this is that people will inevitably alias that string to something useful, and then we can just look at whatever name people are actually using
02:13
<rkirsling>
lol
02:13
<Anthony Bullard>
advantage of this is that people will inevitably alias that string to something useful, and then we can just look at whatever name people are actually using
Pave the cow paths indeed
02:13
<rkirsling>
"delegated bikeshedding"
02:13
<Ashley Claymore>
"the masses have spoken"
02:13
<Anthony Bullard>
I think we have a new standard for APIs we can’t name
02:13
<Jack Works>
advantage of this is that people will inevitably alias that string to something useful, and then we can just look at whatever name people are actually using
this reminds me, does delegate investigated similar utilities on npm?
02:14
<Anthony Bullard>
Someone will still be mad about the UUID we chose
02:14
<hax (HE Shi-Jun)>
computeIfAbsent doesn't really either but I guess it's more obviously going to return something because "compute" suggests returning something
So it's just naming issue? To be honest, in many cases I always expect Map.set would return the value instead of the map itself 🤨
02:15
<Michael Ficarra>
this reminds me, does delegate investigated similar utilities on npm?
I think people mostly just do this pattern inline rather than rely on an npm package for it
02:15
<Jack Works>
Someone will still be mad about the UUID we chose
map['2024-10-09T02:14:57.500Z'](key, value)
02:15
<Jack Works>
I think people mostly just do this pattern inline rather than rely on an npm package for it
yes I write this inline, but we have is-odd on npm so definitely we'll have emplace on npm
02:16
<hax (HE Shi-Jun)>
in ruby i think you'd have it take a block vs take a function, but most langs don't have blocks like that iirc
Really hope JS can have block. Does "Block param" proposal still alive?
02:16
<ljharb>
not that i know of
02:16
<Jack Works>
Really hope JS can have block. Does "Block param" proposal still alive?
realize a block is also a kind of "function"
02:16
<Jack Works>
it contains context and can be executed right?
02:17
<Anthony Bullard>
Really hope JS can have block. Does "Block param" proposal still alive?
Neither a ruby or smalltalk guy, what’s the advantage of blocks?
02:18
<Jack Works>
Neither a ruby or smalltalk guy, what’s the advantage of blocks?

IMO mostly syntax sugar, may look like

x.map { it + 1 }
02:18
<Anthony Bullard>

IMO mostly syntax sugar, may look like

x.map { it + 1 }
Ah shorthand a la Kotlin, Scala, etc
02:21
<ljharb>
but it doesn't have a function stack frame/overhead, ideally
02:21
<hax (HE Shi-Jun)>
The difference between the arrow function and real block may be the real block might support non-local jump (I believe block param proposal has the issue to discuss that, also do expression proposal)
02:22
<Anthony Bullard>
but it doesn't have a function stack frame/overhead, ideally
How do you close over then? Or can it not?
02:23
<ljharb>
it does close over things, true
02:23
<Jack Works>
The difference between the arrow function and real block may be the real block might support non-local jump (I believe block param proposal has the issue to discuss that, also do expression proposal)
non-local jump sound worse
02:24
<Anthony Bullard>
In Kotlin { it + 1 } just desugars to (X) -> X + 1
02:24
<Michael Ficarra>
non-local jump sound worse
tell that to the people who want do expressions to have it
02:25
<bakkot>
huh apparently safari tech preview is already shipping Math.sumPrecise, nice
02:25
<bakkot>
unfortunately a port of my dumb polyfill to C++ instead of something faster
02:25
<Jack Works>
tell that to the people who want do expressions to have it
I also against do expression with return/break/continue
02:25
<hax (HE Shi-Jun)>
non-local jump sound worse
maybe i am wrong about that. I mean something like a.map { if (it != null) compute(it); else break }
02:26
<hax (HE Shi-Jun)>
I also against do expression with return/break/continue
But it's useful in many cases.
02:26
<bakkot>
unfortunately a port of my dumb polyfill to C++ instead of something faster
(apparently the library I linked fails some of my tests; I'll need to try it and see if it can be fixed)
02:26
<Michael Ficarra>
I also against do expression with return/break/continue
yes but unfortunately there's people who are adamant that it allow them
02:26
<bakkot>
also they don't have a fast-path for "array of all numbers" which, I don't know if JSC has a concept of "array of all numbers"
02:26
<Anthony Bullard>
maybe i am wrong about that. I mean something like a.map { if (it != null) compute(it); else break }
So you want a loop, but make it feel functional?
02:27
<bakkot>
(sumPrecise was designed to avoid any user code in the hot loop specifically so that you could have that optimization)
02:27
<rkirsling>
this is a very good question and a very good answer
02:28
<Michael Ficarra>
So you want a loop, but make it feel functional?
more like "you want reduce but you want to call it map"
02:29
<hax (HE Shi-Jun)>
So you want a loop, but make it feel functional?
As I understand, block param proposal has the motivation to make JS more DSL friendly. As such motivate, I say yes.
02:30
<Jack Works>
const { id: Number(id) } = JSON.parse(str)
02:30
<Chris de Almeida>
ljharb: help me understand better stack trace there? that sounds harder to debug
02:31
<ljharb>
currently it gives a stack trace that points to the validation function, and then below that, the validator callsite. there's a possibility for a clearer error imo that points to the function signature and references extractors in the message
02:31
<Chris de Almeida>
hmm
02:31
<Anthony Bullard>
As I understand, block param proposal has the motivation to make JS more DSL friendly. As such motivate, I say yes.
I’m all for desugaring syntax, but I don’t want to get to F# computed expressions levels of complexity
02:33
<hax (HE Shi-Jun)>
more like "you want reduce but you want to call it map"
If only name issue, let's change map to something else , eg. collect? 😂
02:39
<Anthony Bullard>
Is it just me (not on the call), but when the chat dies out suddenly, do you wonder if Matrix just tipped over or was there a break?
02:41
<ptomato>
or everyone is paying real close attention to the slides...
02:41
<Rob Palmer>
Syntactically correct TypeScript will emit JS even if the type checks fail. (And sometimes it will emit JS even if the syntax is invalid.)
02:42
<shu>
right
02:42
<shu>
but that doesn't conflict with what i'm asking
02:46
<hax (HE Shi-Jun)>
Syntactically correct TypeScript will emit JS even if the type checks fail. (And sometimes it will emit JS even if the syntax is invalid.)
yeah , last week someone tell me it's surprise to see import {x} "path" (missing from) still work in ts...
02:47
<Rob Palmer>
My favourite is automatic brace insertion in ts.
02:47
<canadahonk>
My favourite is automatic brace insertion in ts.
what
02:48
<Rob Palmer>
(sorry this is a distraction from current topic - it should be in TDZ)
02:48
<danielrosenwasser>

if you write

if (x) {
//forgot a brace here

TypeScript gracefully recovers parsing, tells you you forgot a brace, and just re-prints it the right way

if (x) {
}
02:48
<Anthony Bullard>
My favourite is automatic brace insertion in ts.
Not the ABI we are looking for
02:57
<Ashley Claymore>

Found this one recently. TS supports Python scoping

function f() {
    if (true)
        type s = string;
        console.log("s" as s);
}
02:58
<danielrosenwasser>

Found this one recently. TS supports Python scoping

function f() {
    if (true)
        type s = string;
        console.log("s" as s);
}
Can you file a bug?
02:59
<danielrosenwasser>
Or I'll file a bug if I can
02:59
<danielrosenwasser>
Either way nice find!
03:00
<Ashley Claymore>
will do!
03:01
<bakkot>
.... what do you even do about that case
03:01
<bakkot>
parse error, I guess?
03:02
<danielrosenwasser>
possibly - we'd probably do it at checking, it gets treated more like an early error
03:02
<Rob Palmer>
it's a graceful parser
03:02
<littledan>
Seems like we should have more discussions in TC39 about the architectures of various implementations, both tools and native
03:03
<littledan>
Like presentations explaining this
03:04
<Michael Ficarra>
@danielrosenwasser allow it, add a grammar alternative like Statement : TypeDeclaration Statement
03:04
<danielrosenwasser>
lmao
03:04
<Michael Ficarra>
😐️ this is my serious face
03:04
<danielrosenwasser>
Statement :: Statement Statement
03:06
<danielrosenwasser>
Likewise, I think it could be helpful to see concretely what the performance concerns are, why certain optimizations are difficult for engine implementers, etc.
03:06
<littledan>
Yeah this feature is as expensive as a method call + array destructuring; I understand the concern about array destructuring being expensive but I don’t know what else engines are imagining that tools would do
03:07
<littledan>
I dont think we should block all features where people have posited impractical optimizations. We certainly would not have done iterator helpers if that were a requirement
03:08
<Ashley Claymore>
Can you file a bug?
https://github.com/microsoft/TypeScript/issues/60175
03:10
<danielrosenwasser>
But I think there's a legitimate "pit of despair" concern. Like imagine you are writing a tool that you want to be reasonably fast and you posit that pattern matching should be as fast as a switch on the class or something like that
03:20
<keith_miller>
I think the concern here is that we're (potentially) offering a very clean but expensive abstraction/idiom that people are trained from other languages to be zero-cost.
03:21
<keith_miller>
If this idiom is no worse in terms of performance than what people would do otherwise then I'm not concerned
03:22
<keith_miller>
But I think adding or encouraging idioms that have known to be bad performance isn't in browser user's interest and that's what I'm trying to reflect.
03:23
<rbuckton>
Summary has been added.
03:24
<rbuckton>
If this idiom is no worse in terms of performance than what people would do otherwise then I'm not concerned
The expectation is that const Point(x, y) = p is no more expensive than const [x, y] = Point[Symbol.customMatcher](p, "list")
03:25
<rbuckton>
though if we end up not finding a solution to iterator destructuring performance and end up going with array-as-object destructuring, const Point(x, y) = p is potentially faster
03:25
<keith_miller>
But it might be more expensive than: if (p instanceof Point) { let x = p.x; let y = p.y ...}
03:25
<keith_miller>
So that's the bar to compare against
03:27
<keith_miller>
And devs just might structure their code differently without this, potentially in a way that's more performant. I'm saying that I want to be convinced they're not.
03:27
<rbuckton>
That doesn't have the expressivity we're seeking. The motivations include custom validation.
03:27
<rbuckton>
The Point example from my slides used brand checking via private names
03:28
<keith_miller>
That's a bummer and I would like to have that validation too but not really my concern, unfortunately.
03:28
<littledan>
I understand the performance concern “this will encourage people to use more array destructuring”. There are a number of possible remedies to that
03:30
<littledan>
One is using this alternative Array-like destructuring semantics, as Ron mentioned. If we want to avoid the allocation altogether, we could also limit extractors to a single argument, and then any nested array destructuring would be explicit.
03:30
<keith_miller>
It's not just the array destructuring although that's part of the concern. It's if the idiom makes sense as a whole.
03:30
<littledan>
The discussion got a bit abstract as to which performance concerns people had. What other ones were there apart from array destructuring cost?
03:31
<littledan>
Or whether the idiom makes sense… it is really common across languages and I have found it useful, so I am wondering if people can make their concerns more concrete.
03:32
<keith_miller>
What other dynamic languages have this?
03:32
<littledan>
Pattern matching/extractors are definitely not linked to static typing. Python and Racket have versions
03:32
<rbuckton>
Python, though they do something slightly different that the pattern matching champions group does not agree with.
03:32
<littledan>
It is just random cultural history that they are linked to functional/statically typed languages
03:33
<littledan>
Erlang is dynamically typed and does tons of pattern matching
03:33
<littledan>
Prolog for that matter, untyped and the ancestor of a lot of these ideas
03:35
<littledan>
Elixir https://hexdocs.pm/elixir/pattern-matching.html#the-match-operator
03:35
<keith_miller>
But are those idioms performant in those languages? Or are they just a cost people are willing to pay for convenience?
03:35
<rbuckton>
Its not really a static vs dynamic language feature. C# uses Deconstruct() for custom extraction, and static type information to do overload disambiguation and argument matching, but compared to this the main difference is that C# uses out parameters, which we don't have, rather than something like iterator destructuring.
03:37
<keith_miller>
I'm not debating the convenience of the idiom. Only its potential performance impact with use.
03:38
<littledan>
I haven’t heard anyone talk about pattern matching as an expensive feature outside of the array destructuring issue in any other language. Some functional statically typed languages do fancy things optimizing trees of conditionals for pattern matching but others don’t.
03:38
<rbuckton>
The pattern matching proposal has some interesting mechanisms it plans to employ related to caching that have the side benefit of improving performance in a disjunction or multiple match-legs.
03:39
<keith_miller>
Then that should make proving it's performant easy!
03:39
<keith_miller>
I'm not saying it's not performant only that I'd like to see non-trivial real world use cases benchmarked.
03:39
<rbuckton>

So in pattern matching something like :

match (x) {
  Point(0, 0): ...,
  Point(>0, 0): ...,
}

could potentially only evaluate the custom matcher once and reuse the extracted elements on both match legs.

03:40
<littledan>
Ruby pattern matching: https://docs.ruby-lang.org/en/master/syntax/pattern_matching_rdoc.html
03:42
<littledan>
I'm not saying it's not performant only that I'd like to see non-trivial real world use cases benchmarked.
Yeah this is a reasonable and good request but to be able to do it, it would help to understand what people think could go wrong. Ron has posited some possible complex optimizations but those cases could be optimized or made slower with or without pattern matching.
03:42
<rbuckton>
Pattern matching caching is something I'm not looking forward to desugaring mostly due to the emit size of the desugaring. I'm hoping I can stash a lot of it in emit helpers in tslib to cut down on bundle size.
03:44
<littledan>

So in pattern matching something like :

match (x) {
  Point(0, 0): ...,
  Point(>0, 0): ...,
}

could potentially only evaluate the custom matcher once and reuse the extracted elements on both match legs.

I guess this is something we will debate in the broader pattern matching proposal. I was hoping we wouldn’t do the kind of complex caching that some versions of pattern matching had—this might add more cost than value, given that it has to be deterministic and consistent
03:44
<littledan>
And the >0 syntax is a whole other topic!
03:45
<rbuckton>
It's not part of the core proposal, to be clear. Its something I'd like to see but we're pushing it to a follow-on.
03:45
<rbuckton>
It's extremely useful in C#
03:50
<littledan>
If array destructuring is expensive and impractical to optimize, should we in TC39 be discouraging its use? For example, by encouraging transpilers to transform it into object destructuring (treating it as array-like)?
03:51
<littledan>
I don’t like this idea personally but if this is such a big problem that we can’t build other features on it…
04:00
<bakkot>
yes, code which cares about performance does specifically write { 0: foo, 1: bar } = instead of [foo, bar] =
04:00
<bakkot>
transpilers can't do this without type information
04:01
<Ashley Claymore>
I wonder if it be web-compat, and also more performant, to change iterator destructuring to be specced as property access if the RHS passes Array.isArray ? i.e. assume the array's iterator will be the default one
04:01
<rbuckton>
It's significantly faster to do const { 0: x, 1: setX } = useState() in React, but its rare to see that done.
04:02
<keith_miller>
That's an interesting idea! No idea if it's compatible though.
04:02
<bakkot>
I wonder if it be web-compat, and also more performant, to change iterator destructuring to be specced as property access if the RHS passes Array.isArray ? i.e. assume the array's iterator will be the default one
... tempting... and I would guess probably compatible?
04:02
<bakkot>
though it might need to be "isArray and also not a proxy", potentially
04:02
<bakkot>
proxies :(
04:03
<canadahonk>
I do basically this internally (sorry spec)
04:04
<canadahonk>
it definitely fails some test262 tests though
04:04
<rbuckton>
How many people use for..of over arrays today? I assume that still isn't optimized for Array vs other iterators.
04:05
<bakkot>
I am almost certain that's optimized in JSC, at least
04:05
<bakkot>
and would kind of expect it to be in V8 but it's been a while since I looked
04:05
<shu>
we suck at it still i think
04:06
<rbuckton>
If you can optimize for..of why not const [x, y] =?
04:07
<shu>
we can do something for const [x,y] probably, but it's not going to be parity with object destructuring, will have a perf cliff for non-Arrays and a bunch of other bailout paths (which is also a increased attack surface)
04:07
<shu>
there're a lot of undesirables hidden behind "why not optimize this other pattern"
04:09
<rbuckton>
I expect most destructuring is array to array, but I don't have metrics to back that up.
04:09
<rbuckton>
I'd be perfectly happy with Array being fast and other iterators being slower, since the alternative is both are slower.
04:10
<shu>
yeah i agree it's worth optimizing that?
04:10
<rbuckton>
React alone should be enough of a reason to optimize array to array destructuring, but I'm working on some benchmarking to get an understanding of the actual effect it has.
04:11
<keith_miller>
It is very optimized in JSC (modulo the iterator object itself being allocated)
04:11
<Rob Palmer>
I thought V8 had already done that
04:11
<justingrant>
7 years, wow
You think it's wow from the outside, imagine feeling those years from the inside! Although I only joined the Temporal champion 4.5 years ago so I'm still a fresh n00b. 😀
04:11
<shu>
i don't actually know, lemme see
04:11
<littledan>
Yes they can—tsc does it in some emit modes unconditionally! (When emitting es5)
04:12
<bakkot>
sorry, without also breaking some other code
04:12
<Jack Works>
How many people use for..of over arrays today? I assume that still isn't optimized for Array vs other iterators.
almost all of my code is for..of even it might be slow. readibility > performance when the code is not in a hot path
04:12
<Ashley Claymore>
https://docs.google.com/document/d/1hWb-lQW4NSG9yRpyyiAA_9Ktytd5lypLnVLhPX9vamE/edit?tab=t.0#heading=h.9ss45aibqpw2
04:12
<rbuckton>
TSC only does that for --target ES5 where there is no expectation of Symbol.iterator.
04:12
<littledan>
sorry, without also breaking some other code
If you care about breaking other code, then you might need runtime support to check that no one messed with Array.prototype
04:12
<rbuckton>
And we have --downevelIteration to emulate actual ES2015+ semantics.
04:13
<littledan>
Yes sorry that wasn’t meant as a tsc critique
04:13
<littledan>
But generally: if we want transpilers to do unsound things (like around TDZ) that is something we as a committee can say
04:14
<rbuckton>
Even in the --downlevelIteration case we use a runtime helper to emulate an iterator over an array, rather than bake that into the actual loop.
04:14
<rbuckton>
so the emit remains the same regardless as to the type of the iterated object
04:14
<shu>
https://docs.google.com/document/d/1hWb-lQW4NSG9yRpyyiAA_9Ktytd5lypLnVLhPX9vamE/edit?tab=t.0#heading=h.9ss45aibqpw2
oh cool. looks like we didn't actually land any of the talked about solutions though? the bytecode still looks pretty bad to me
04:14
<littledan>
I dunno if we should do such unsound downlevelings but I don’t know what else would actually meet what the browsers seem to be expecting
04:14
<shu>
the quickest way to make sure that happens is to make it show up in speedometer or the new jetstream
04:15
<Ashley Claymore>

is it a really bad idea to have a 3rd form of destructuring for array-like?

const |a, b, c| = multiReturnFunction(); // probably a better token is available
// is
const { 0: a, 1: b, 2: c } = multiReturnFunction();
04:16
<keith_miller>
The expectation is that const Point(x, y) = p is no more expensive than const [x, y] = Point[Symbol.customMatcher](p, "list")
Sorry getting back to this but while yes const Point(x, y) = p isn't more expensive than that const Optional.Some(Point.2D(x, y)) = o would likely decode to 20-30 byte codes in JSC
04:16
<rbuckton>
let |a, is ambiguous
04:17
<Ashley Claymore>
one R&T design was [| a, b, c |], maybe could be more like that?
04:18
<ljharb>
Sorry getting back to this but while yes const Point(x, y) = p isn't more expensive than that const Optional.Some(Point.2D(x, y)) = o would likely decode to 20-30 byte codes in JSC
why not to const [x, y] = Optional.some[Symbol.customMatcher](Point[Symbol.customMatcher](p, 'list'), 'list');?
04:18
<shu>
i'll be somewhat blunt: i continue to see the line of argument from extractor-proponents to be "for these technical reasons it's unrealistic to do this in tools" that's fine! but that doesn't imply "engines will do it"
04:18
<shu>
i think the counterfactual that "people will write things that are even slower" is also just not true in this case, because pattern matching and extractors don't exist?
04:19
<ljharb>
the things those will be used for are already done, just in other ways
04:19
<shu>
and if they don't want to write that verbose example that jordan posted above, that's good! the grossness of the example correlates with the slowness
04:19
<ljharb>
pattern matching is done with function calls and if/else and/or switch, extractors with separate function calls
04:19
<shu>
that seems good to me
04:19
<shu>
their verbosity gives a hint to the work they're doing
04:20
<ljharb>
i'm not sure the point of programming languages is to have verbosity correlate to cost?
04:20
<nicolo-ribaudo>
i'll be somewhat blunt: i continue to see the line of argument from extractor-proponents to be "for these technical reasons it's unrealistic to do this in tools"

that's fine! but that doesn't imply "engines will do it"
Extractors are actually something that would be easy to do in Babel, just not fast/small
04:20
<rbuckton>
Pattern matching results in far more readable code than the current approach.
04:20
<shu>
if a terser and more readable thing can be a near zero-cost abstraction, that's great
04:20
<rbuckton>
The verbosity makes it harder to reason over.
04:20
<shu>
if it can't be, we gave our position yesterday on the cost to users
04:21
<Bradford Smith>
My concern with Extractors is that they seem overly clever to me. They hide what's happening from the developer too much, leading them to incorrect ideas about how expensive they are.
04:21
<shu>
i'm not sure the point of programming languages is to have verbosity correlate to cost?
the point of programming languages is to write applications that have some value to users of the applications
04:21
<ljharb>
indeed
04:21
<rbuckton>
pattern matching can be more efficient than the alternative if we have sufficient caching and reuse across multiple branches of the pattern.
04:21
<keith_miller>

Sure but that's still 48 byte codes in JSC today:

[x, y] = Optional.some[Symbol.customMatcher](Point[Symbol.customMatcher](p, 'list'), 'list');
<global>#BS5CtO:[0x130538130->0x10e071188, NoneGlobal, 265]: 48 instructions (0 16-bit instructions, 0 32-bit instructions, 27 instructions with metadata); 361 bytes (96 metadata bytes); 1 parameter(s); 20 callee register(s); 5 variable(s); scope at loc4

bb#1
Predecessors: [ ]
[   0] enter
[   1] mov                dst:loc5, src:Undefined(const0)
[   4] resolve_scope      dst:loc7, scope:loc4, var:0, resolveType:GlobalProperty, localScopeDepth:0
[  11] get_from_scope     dst:loc8, scope:loc7, var:0, getPutInfo:2048<ThrowIfNotFound|GlobalProperty|NotInitialization|NotStrictMode>, localScopeDepth:0, offset:0, valueProfile:1
[  20] get_by_id          dst:loc6, base:loc8, property:1, valueProfile:2
[  26] resolve_scope      dst:loc8, scope:loc4, var:2, resolveType:GlobalProperty, localScopeDepth:0
[  33] get_from_scope     dst:loc9, scope:loc8, var:2, getPutInfo:2048<ThrowIfNotFound|GlobalProperty|NotInitialization|NotStrictMode>, localScopeDepth:0, offset:0, valueProfile:3
[  42] get_by_id          dst:loc7, base:loc9, property:3, valueProfile:4
[  48] get_by_val         dst:loc5, base:loc6, property:loc7, valueProfile:5
[  54] mov                dst:loc10, src:loc6
[  57] resolve_scope      dst:loc11, scope:loc4, var:4, resolveType:GlobalProperty, localScopeDepth:0
[  64] get_from_scope     dst:loc11, scope:loc11, var:4, getPutInfo:2048<ThrowIfNotFound|GlobalProperty|NotInitialization|NotStrictMode>, localScopeDepth:0, offset:0, valueProfile:6
[  73] resolve_scope      dst:loc13, scope:loc4, var:2, resolveType:GlobalProperty, localScopeDepth:0
[  80] get_from_scope     dst:loc14, scope:loc13, var:2, getPutInfo:2048<ThrowIfNotFound|GlobalProperty|NotInitialization|NotStrictMode>, localScopeDepth:0, offset:0, valueProfile:7
[  89] get_by_id          dst:loc12, base:loc14, property:3, valueProfile:8
[  95] get_by_val         dst:loc9, base:loc11, property:loc12, valueProfile:9
[ 101] mov                dst:loc14, src:loc11
[ 104] resolve_scope      dst:loc13, scope:loc4, var:5, resolveType:GlobalProperty, localScopeDepth:0
[ 111] get_from_scope     dst:loc13, scope:loc13, var:5, getPutInfo:2048<ThrowIfNotFound|GlobalProperty|NotInitialization|NotStrictMode>, localScopeDepth:0, offset:0, valueProfile:10
[ 120] mov                dst:loc12, src:String (atomic),8Bit:(1),length:(4): list, StructureID: 16976(const1)
[ 123] call               dst:loc9, callee:loc9, argc:3, argv:20, valueProfile:11
[ 130] mov                dst:loc8, src:String (atomic),8Bit:(1),length:(4): list, StructureID: 16976(const1)
[ 133] call               dst:loc5, callee:loc5, argc:3, argv:16, valueProfile:12
[ 140] get_by_id          dst:loc8, base:loc5, property:6, valueProfile:13
[ 146] mov                dst:loc10, src:loc5
[ 149] iterator_open      iterator:loc6, next:loc7, symbolIterator:loc8, iterable:loc10, stackOffset:16, iterableValueProfile:14, iteratorValueProfile:15, nextValueProfile:16
[ 159] mov                dst:loc10, src:loc6
[ 162] iterator_next      done:loc8, value:loc9, iterable:loc5, next:loc7, iterator:loc10, stackOffset:16, nextResultValueProfile:17, doneValueProfile:18, valueValueProfile:19
[ 173] jfalse             condition:loc8, targetLabel:6(->179)
Successors: [ #3 #2 ]

bb#2
Predecessors: [ #1 ]
[ 176] mov                dst:loc9, src:Undefined(const0)
Successors: [ #3 ]

bb#3
Predecessors: [ #1 #2 ]
[ 179] resolve_scope      dst:loc10, scope:loc4, var:7, resolveType:GlobalProperty, localScopeDepth:0
[ 186] put_to_scope       scope:loc10, var:7, value:loc9, getPutInfo:1050624<DoNotThrowIfNotFound|GlobalProperty|NotInitialization|NotStrictMode>, symbolTableOrScopeDepth:0, offset:0
[ 194] jtrue              condition:loc8, targetLabel:20(->214)
Successors: [ #5 #4 ]

bb#4
Predecessors: [ #3 ]
[ 197] mov                dst:loc10, src:loc6
[ 200] iterator_next      done:loc8, value:loc9, iterable:loc5, next:loc7, iterator:loc10, stackOffset:16, nextResultValueProfile:20, doneValueProfile:21, valueValueProfile:22
[ 211] jfalse             condition:loc8, targetLabel:6(->217)
Successors: [ #6 #5 ]

bb#5
Predecessors: [ #3 #4 ]
[ 214] mov                dst:loc9, src:Undefined(const0)
Successors: [ #6 ]

bb#6
Predecessors: [ #4 #5 ]
[ 217] resolve_scope      dst:loc10, scope:loc4, var:8, resolveType:GlobalProperty, localScopeDepth:0
[ 224] put_to_scope       scope:loc10, var:8, value:loc9, getPutInfo:1050624<DoNotThrowIfNotFound|GlobalProperty|NotInitialization|NotStrictMode>, symbolTableOrScopeDepth:0, offset:0
[ 232] jtrue              condition:loc8, targetLabel:31(->263)
Successors: [ #10 #7 ]

bb#7
Predecessors: [ #6 ]
[ 235] get_by_id          dst:loc9, base:loc6, property:9, valueProfile:23
[ 241] jundefined_or_null value:loc9, targetLabel:22(->263)
Successors: [ #10 #8 ]

bb#8
Predecessors: [ #7 ]
[ 244] mov                dst:loc12, src:loc6
[ 247] call               dst:loc10, callee:loc9, argc:1, argv:18, valueProfile:24
[ 254] is_object          dst:loc13, operand:loc10
[ 257] jtrue              condition:loc13, targetLabel:6(->263)
Successors: [ #10 #9 ]

bb#9
Predecessors: [ #8 ]
[ 260] throw_static_error message:String (atomic),8Bit:(1),length:(43): Iterator result interface is not an object., StructureID: 16976(const2), errorType:TypeError
Successors: [ ]

bb#10
Predecessors: [ #6 #7 #8 ]
[ 263] end                value:loc5
Successors: [ ]


Identifiers:
  id0 = Optional
  id1 = some
  id2 = Symbol
  id3 = customMatcher
  id4 = Point
  id5 = p
  id6 = Symbol.iterator
  id7 = x
  id8 = y
  id9 = return

Constants:
   k0 = Undefined
   k1 = String (atomic),8Bit:(1),length:(4): list, StructureID: 16976
   k2 = String (atomic),8Bit:(1),length:(43): Iterator result interface is not an object., StructureID: 16976
04:21
<ljharb>
and readable code correlates to that value, imo often more highly than the performance of that application
04:21
<shu>
i disagree so strongly on that point
04:22
<ljharb>
that is clear :-)
04:23
<bakkot>
I might believe that claim in C, which is hard to write correctly and where bugs can do arbitrarily bad things, but I certainly do not agree with that claim for web apps written in JS
04:24
<keith_miller>
I might believe that claim in C, which is hard to write correctly and where bugs can do arbitrarily bad things, but I certainly do not agree with that claim for web apps written in JS
Are you agreeing with Jordan or Shu? lol
04:24
<ljharb>
it's not a claim that it's always the case, to be clear, and obv some threshold of slowness will always cancel out the other benefits.
04:24
<keith_miller>
Which is a lot by JSC standards
04:24
<keith_miller>
This function would be invalid for inlining from one binding alone
04:25
<bakkot>
I am agreeing with shu
04:26
<bakkot>
I think that webapps being slow is the cause of much more pain than webapps being unclear to their maintainers
04:26
<keith_miller>
why not to const [x, y] = Optional.some[Symbol.customMatcher](Point[Symbol.customMatcher](p, 'list'), 'list');?
Pulling this into the main channel. This would be sufficiently complex (48 byte codes) that this would be no longer considered for inlining under JSC's current heuristic.
04:27
<keith_miller>
From one statement
04:27
<keith_miller>
Which I don't think devs would expect
04:28
<rbuckton>
JSC inlining is limited to the statement level?
04:28
<keith_miller>
It's limited by the number of bytes in the instruction stream
04:28
<keith_miller>
IIRC, it's 170 bytes
04:30
<Michael Ficarra>

is it a really bad idea to have a 3rd form of destructuring for array-like?

const |a, b, c| = multiReturnFunction(); // probably a better token is available
// is
const { 0: a, 1: b, 2: c } = multiReturnFunction();
const Array(a, b, c) = ...?
04:31
<rbuckton>
If extractors end up needing to use array-as-object destructuring, then yeah, that's a possibility.
04:33
<keith_miller>
A lot of that is resolving the various properties needed though not the array destructuring
04:34
<keith_miller>
I encourage you to look at that bytecode sequence and really think about all the subtle work that has to happen to resolve that binding.
04:35
<shu>
i feel like folks haven't really internalized the "complexity is moved, not removed" point we tried to make yet
04:36
<rbuckton>
And the bytecode for const [[[x, y]]] = foo given it results in three lookups to Symbol.iterator? I understand extractors do that as well, I'm just curious.
04:36
<Michael Ficarra>
@shu this could literally be a special form that does remove the complexity, different from const window.Array(a, b, c) = ...
04:37
<keith_miller>

const [[[x, y]]] = foo
<global>#CzC5ob:[0x130538220->0x10e071488, NoneGlobal, 285]: 59 instructions (0 16-bit instructions, 0 32-bit instructions, 22 instructions with metadata); 381 bytes (96 metadata bytes); 1 parameter(s); 26 callee register(s); 5 variable(s); scope at loc4

bb#1
Predecessors: [ ]
[ 0] enter
[ 1] mov dst:loc5, src:Undefined(const0)
[ 4] resolve_scope dst:loc6, scope:loc4, var:0, resolveType:GlobalProperty, localScopeDepth:0
[ 11] get_from_scope dst:loc6, scope:loc6, var:0, getPutInfo:2048<ThrowIfNotFound|GlobalProperty|NotInitialization|NotStrictMode>, localScopeDepth:0, offset:0, valueProfile:1
[ 20] get_by_id dst:loc9, base:loc6, property:1, valueProfile:2
[ 26] mov dst:loc10, src:loc6
[ 29] iterator_open iterator:loc7, next:loc8, symbolIterator:loc9, iterable:loc10, stackOffset:16, iterableValueProfile:3, iteratorValueProfile:4, nextValueProfile:5
[ 39] mov dst:loc12, src:loc7
[ 42] iterator_next done:loc9, value:loc10, iterable:loc6, next:loc8, iterator:loc12, stackOffset:18, nextResultValueProfile:6, doneValueProfile:7, valueValueProfile:8
[ 53] jfalse condition:loc9, targetLabel:6(->59)
Successors: [ #3 #2 ]

bb#2
Predecessors: [ #1 ]
[ 56] mov dst:loc10, src:Undefined(const0)
Successors: [ #3 ]

bb#3
Predecessors: [ #1 #2 ]
[ 59] get_by_id dst:loc13, base:loc10, property:1, valueProfile:9
[ 65] mov dst:loc14, src:loc10
[ 68] iterator_open iterator:loc11, next:loc12, symbolIterator:loc13, iterable:loc14, stackOffset:20, iterableValueProfile:10, iteratorValueProfile:11, nextValueProfile:12
[ 78] mov dst:loc16, src:loc11
[ 81] iterator_next done:loc13, value:loc14, iterable:loc10, next:loc12, iterator:loc16, stackOffset:22, nextResultValueProfile:13, doneValueProfile:14, valueValueProfile:15
[ 92] jfalse condition:loc13, targetLabel:6(->98)
Successors: [ #5 #4 ]

bb#4
Predecessors: [ #3 ]
[ 95] mov dst:loc14, src:Undefined(const0)
Successors: [ #5 ]

bb#5
Predecessors: [ #3 #4 ]
[ 98] get_by_id dst:loc17, base:loc14, property:1, valueProfile:16
[ 104] mov dst:loc18, src:loc14
[ 107] iterator_open iterator:loc15, next:loc16, symbolIterator:loc17, iterable:loc18, stackOffset:24, iterableValueProfile:17, iteratorValueProfile:18, nextValueProfile:19
[ 117] mov dst:loc20, src:loc15
[ 120] iterator_next done:loc17, value:loc18, iterable:loc14, next:loc16, iterator:loc20, stackOffset:26, nextResultValueProfile:20, doneValueProfile:21, valueValueProfile:22
[ 131] jfalse condition:loc17, targetLabel:6(->137)
Successors: [ #7 #6 ]

bb#6
Predecessors: [ #5 ]
[ 134] mov dst:loc18, src:Undefined(const0)
Successors: [ #7 ]

bb#7
Predecessors: [ #5 #6 ]
[ 137] resolve_scope dst:loc19, scope:loc4, var:2, resolveType:GlobalProperty, localScopeDepth:0
[ 144] put_to_scope scope:loc19, var:2, value:loc18, getPutInfo:1049600<DoNotThrowIfNotFound|GlobalProperty|ConstInitialization|NotStrictMode>, symbolTableOrScopeDepth:0, offset:0
[ 152] jtrue condition:loc17, targetLabel:20(->172)
Successors: [ #9 #8 ]

bb#8
Predecessors: [ #7 ]
[ 155] mov dst:loc20, src:loc15
[ 158] iterator_next done:loc17, value:loc18, iterable:loc14, next:loc16, iterator:loc20, stackOffset:26, nextResultValueProfile:23, doneValueProfile:24, valueValueProfile:25
[ 169] jfalse condition:loc17, targetLabel:6(->175)
Successors: [ #10 #9 ]

bb#9
Predecessors: [ #7 #8 ]
[ 172] mov dst:loc18, src:Undefined(const0)
Successors: [ #10 ]

bb#10
Predecessors: [ #8 #9 ]
[ 175] resolve_scope dst:loc19, scope:loc4, var:3, resolveType:GlobalProperty, localScopeDepth:0
[ 182] put_to_scope scope:loc19, var:3, value:loc18, getPutInfo:1049600<DoNotThrowIfNotFound|GlobalProperty|ConstInitialization|NotStrictMode>, symbolTableOrScopeDepth:0, offset:0
[ 190] jtrue condition:loc17, targetLabel:31(->221)
Successors: [ #14 #11 ]

bb#11
Predecessors: [ #10 ]
[ 193] get_by_id dst:loc18, base:loc15, property:4, valueProfile:26
[ 199] jundefined_or_null value:loc18, targetLabel:22(->221)
Successors: [ #14 #12 ]

bb#12
Predecessors: [ #11 ]
[ 202] mov dst:loc20, src:loc15
[ 205] call dst:loc19, callee:loc18, argc:1, argv:26, valueProfile:27
[ 212] is_object dst:loc21, operand:loc19
[ 215] jtrue condition:loc21, targetLabel:6(->221)
Successors: [ #14 #13 ]

bb#13
Predecessors: [ #12 ]
[ 218] throw_static_error message:String (atomic),8Bit:(1),length:(43): Iterator result interface is not an object., StructureID: 16976(const1), errorType:TypeError
Successors: [ ]

bb#14
Predecessors: [ #10 #11 #12 ]
[ 221] jtrue condition:loc13, targetLabel:31(->252)
Successors: [ #18 #15 ]

bb#15
Predecessors: [ #14 ]
[ 224] get_by_id dst:loc14, base:loc11, property:4, valueProfile:28
[ 230] jundefined_or_null value:loc14, targetLabel:22(->252)
Successors: [ #18 #16 ]

bb#16
Predecessors: [ #15 ]
[ 233] mov dst:loc16, src:loc11
[ 236] call dst:loc15, callee:loc14, argc:1, argv:22, valueProfile:29
[ 243] is_object dst:loc17, operand:loc15
[ 246] jtrue condition:loc17, targetLabel:6(->252)
Successors: [ #18 #17 ]

bb#17
Predecessors: [ #16 ]
[ 249] throw_static_error message:String (atomic),8Bit:(1),length:(43): Iterator result interface is not an object., StructureID: 16976(const1), errorType:TypeError
Successors: [ ]

bb#18
Predecessors: [ #14 #15 #16 ]
[ 252] jtrue condition:loc9, targetLabel:31(->283)
Successors: [ #22 #19 ]

bb#19
Predecessors: [ #18 ]
[ 255] get_by_id dst:loc10, base:loc7, property:4, valueProfile:30
[ 261] jundefined_or_null value:loc10, targetLabel:22(->283)
Successors: [ #22 #20 ]

bb#20
Predecessors: [ #19 ]
[ 264] mov dst:loc12, src:loc7
[ 267] call dst:loc11, callee:loc10, argc:1, argv:18, valueProfile:31
[ 274] is_object dst:loc13, operand:loc11
[ 277] jtrue condition:loc13, targetLabel:6(->283)
Successors: [ #22 #21 ]

bb#21
Predecessors: [ #20 ]
[ 280] throw_static_error message:String (atomic),8Bit:(1),length:(43): Iterator result interface is not an object., StructureID: 16976(const1), errorType:TypeError
Successors: [ ]

bb#22
Predecessors: [ #18 #19 #20 ]
[ 283] end value:loc5
Successors: [ ]

Identifiers:
id0 = foo
id1 = Symbol.iterator
id2 = x
id3 = y
id4 = return

Constants:
k0 = Undefined
k1 = String (atomic),8Bit:(1),length:(43): Iterator result interface is not an object., StructureID: 16976

04:37
<Michael Ficarra>
please don't dump walls of text into Matrix, make a gist or something
04:38
<keith_miller>
I put it in a thread for that reason?
04:39
<rbuckton>
Does the bytecode for the Optional.some[Symbol.customMatcher] include the cost of looking up customMatcher on Symbol? A native implementation would be able to avoid that because it would be using a known symbol.
04:39
<Michael Ficarra>
I put it in a thread for that reason?
oh I think my client doesn't do threads
04:40
<keith_miller>
oh I think my client doesn't do threads
Ah fair, sorry. I assumed that was the standard.
04:40
<hax (HE Shi-Jun)>
and readable code correlates to that value, imo often more highly than the performance of that application
I agree with Jordan, extractor give u a simple abstraction and shorter code. such feature will improve the code quality generally (not only perf, but also many other aspects).
04:40
<Jack Works>
Does the bytecode for the Optional.some[Symbol.customMatcher] include the cost of looking up customMatcher on Symbol? A native implementation would be able to avoid that because it would be using a known symbol.
that's just 1~3 steps i guess
04:40
<keith_miller>
Sure, that would be avoided but that's not too much of the code generated
04:46
<keith_miller>
I think the browser vendors are pushing back on the idea that it gives you perf
04:46
<keith_miller>
It's not a magic box where things become faster because you want it to
04:46
<rbuckton>
At least extractors are not a hidden cost you stumble into, unlike getters/setters or let/const TDZ. You don't pay the cost if you don't use extractors, and so long as developers are aware of the cost (and that cost is not absurdly high), developers can balance the pros and cons and fall back to existing behaviors for high performance scenarios. I think part of concern is that developers might blindly adopt extractors without considering the cost?
04:46
<Justin Ridgewell>
oh I think my client doesn't do threads
Cinny shows “threaded reply” under the username
04:46
<shu>
i think extractors are totally a hidden cost
04:47
<yulia>
At least extractors are not a hidden cost you stumble into, unlike getters/setters or let/const TDZ. You don't pay the cost if you don't use extractors, and so long as developers are aware of the cost (and that cost is not absurdly high), developers can balance the pros and cons and fall back to existing behaviors for high performance scenarios. I think part of concern is that developers might blindly adopt extractors without considering the cost?
extractors are quite similar to let/const, they hide the cost of the operation behind what looks like light weight syntax
04:47
<shu>
here's a really simple, clean looking code (multiple delegates have said as such, i also think it looks very clean and nice) that hides a pretty expensive operation that doesn't exist in other languages that inspired this feature!
04:47
<Michael Ficarra>
@yulia no more so than positional destructuring
04:47
<keith_miller>
Yeah the syntax is 💯 hiding the cost! That's exactly what I was trying to show!
04:47
<rbuckton>
The syntax looks like a function call, part of the benefit of that is that the expectation is that it will have the cost of a function call.
04:48
<shu>
do you think a rust or haskell programmer look at pattern matching and think that's a function call?
04:48
<canadahonk>
couldn't the same be said with array destructuring?
04:48
<bakkot>
yes
04:48
<shu>
yeah?
04:48
<yulia>
yes,
04:48
<shu>
array destructuring is terrible
04:48
<canadahonk>
welp
04:48
<keith_miller>
@yulia no more so than positional destructuring
Sure and I think engines would consider that was a mistake
04:48
<hax (HE Shi-Jun)>
A function calling is also a very simple syntax (and can hide cost). I don't see there is any diff.
04:49
<keith_miller>
In every language a function call is a significant cost
04:50
<keith_miller>
Maybe if you're clever you can get the compiler to remove a lot of that but IMO devs know that's on them to verify
04:50
<Aki>
"other parts of the web platform" /me perks up
04:50
<Chris de Almeida>
the good parts or... ?
04:51
<Marja HÜlttä>
function calls and things like array destructing / extractors are also completely different levels of fundamental; can't really imagine a modern language without functions, but the other 2 are much more optional
04:51
<rkirsling>
amusing overload of "javascript core" lol
04:51
<rkirsling>
(yes it was clear in context hehe)
04:51
<keith_miller>
Yeah, I got really confused for a sec lol
04:51
<yulia>
sooorrryyyy my brain is so dead
04:51
<rbuckton>
Maybe if you're clever you can get the compiler to remove a lot of that but IMO devs know that's on them to verify
If they're expected to know that const p = Point(x, y) has a cost, they won't be expected to know that const Point(x, y) = p does as well?
04:51
<Bradford Smith>
Also, there is no restriction in the Extractors proposal on just how expensive that function may be. It could do anything. The examples assume they will always "undo" a constructor call, but there's no reason to believe it would stay restricted to that in practice.
04:52
<rbuckton>
That's part of the reason for the duality of the syntax. Extraction mirrors application.
04:52
<Bradford Smith>
only by convention, though. It's not enforceable.
04:52
<rbuckton>
application can be arbitrarily expensive because its user code. Extraction can be arbitrarily expensive because its user code.
04:53
<keith_miller>
No because other bindings in the language don't have a cost. let/const is a great example of this in the sense that people didn't expect there would be a cost there.
04:53
<hax (HE Shi-Jun)>
If a specific extracting logic is complex and cost , then it always there, people just write it in some other form, and generally speaking, average programmers normally write much slower code repeatly and every place. On the other side, extractor allow senior programmer factor the logic and do optimization in single place and provide other an easy to adoption syntax.
04:53
<Bradford Smith>
My point is, part of the assumption of improved readability is assuming that the feature will be used in a particular way. I expect it would be abused to make code "magically" shorter.
04:54
<rkirsling>
ngl if I'm in JS author mode, my brain wants to view destructuring as zero-cost
04:54
<rbuckton>
No because other bindings in the language don't have a cost. let/const is a great example of this in the sense that people didn't expect there would be a cost there.
Yes they do, const [x, y] has a cost. Extractors have an obvious cost because they are explicitly designed to run user code, that's the whole point.
04:55
<keith_miller>
I think it's non-obvious. Maybe this would be worth a developer study?
04:55
<rbuckton>
Most developers don't think about iteration cost when they use const [x, y] because the possibility of iteration being expensive is hidden on the RHS, not the LHS. Extractors put that cost in your face by being on the LHS.
04:55
<hax (HE Shi-Jun)>
ngl if I'm in JS author mode, my brain wants to view destructuring as zero-cost
I only expect builtin syntax like object destructuing and array destructing to be fast enough. But extractor means custom matcher and it just like function, and I don't expect it is zero cost.
04:55
<keith_miller>
IIRC, Mozilla (not asking them to do it again) did this in the past and it revealed a lot of interesting insights.
04:57
<yulia>
(we did what now?)
04:58
<yulia>
... user study? yeah thats something we have a whole TG for please get in touch!
04:58
<shu>
user study for this hidden cost question
04:58
<yulia>
we can get students to do it
04:58
<yulia>
(im not doing it, no time these days, but there are eager folks)
04:59
<rbuckton>
That could be very helpful, though I'm not sure how to get that process started.
04:59
<yulia>
you can make an issue in https://github.com/tc39/tg5/issues or add it here: https://github.com/tc39/tg5/issues/29
05:00
<yulia>
then at our next meeting we will bring it up as a request for help from the committee, and we will see if one of the universities wants to take it up
05:02
<rbuckton>
Thanks, I'll look into this after plenary.
05:07
<Michael Ficarra>
hot take: I'm kinda okay with engine divergence on stupid stuff that nobody should ever be observing
05:07
<Michael Ficarra>
maybe that's a lukewarm take, I dunno
05:08
<Ashley Claymore>
it's like natural fuzz testing of apps
05:14
<shu>
i think this is a ArrayBuffer-specific thing
05:14
<shu>
like in general deviance is particularly bad for AB and TAs because of the whole Khronos thing
05:15
<rbuckton>
Only stage 4 candidate of the day, there were like 4 yesterday
05:16
<keith_miller>
Yeah AB is a pants on head crazy place
05:16
<rkirsling>
detach all the things
05:16
<shu>
in particular that bug from the presentation is because what used to be a ToInteger changed to a ToIndex sometime in 2016
05:16
<shu>
https://github.com/tc39/ecma262/pull/410
05:16
<Chris de Almeida>
Only stage 4 candidate of the day, there were like 4 yesterday
it's the last one remaining for this meeting, is what I meant
05:17
<shu>
like, it looks like SM was intimately involved with that PR at the time, which is probably why they're compliant
05:17
<shu>
and this just never got implemented in JSC and V8 for whatever reason at the time
05:17
<shu>
hot take: I'm kinda okay with engine divergence on stupid stuff that nobody should ever be observing
yeah, experience with ABs and TAs show that this is surprisingly robust?
05:22
<nicolo-ribaudo>
I was talking with an MDN writer -- I think we should make MDN a stage 4 requirement. MDN is very happy to write docs by themselves, but we don't really have a process to coordinate. Their current process is "regularly check what Firefox implements", which means that sometimes Stage 4 proposals (thus, meant to be used by developers) have no docs
05:23
<yulia>
we have a meta issue for all tc39 proposals
05:23
<yulia>
https://bugzilla.mozilla.org/show_bug.cgi?id=1435811
05:23
<yulia>
and each proposal (now) has a meta that documentation watches
05:24
<ryzokuken>
I was talking with an MDN writer -- I think we should make MDN a stage 4 requirement. MDN is very happy to write docs by themselves, but we don't really have a process to coordinate. Their current process is "regularly check what Firefox implements", which means that sometimes Stage 4 proposals (thus, meant to be used by developers) have no docs
it has come up a few times in the past before but TG2 already does this
05:24
<Michael Ficarra>
I was talking with an MDN writer -- I think we should make MDN a stage 4 requirement. MDN is very happy to write docs by themselves, but we don't really have a process to coordinate. Their current process is "regularly check what Firefox implements", which means that sometimes Stage 4 proposals (thus, meant to be used by developers) have no docs
we can't make something a requirement if it is entirely out of our control
05:24
<ryzokuken>
and not at Stage 4
05:25
<ryzokuken>
so yeah this is not unprecedented and we just need to bring it to agenda to get into the process doc I guess
05:25
<Andreu Botella (at TC39, 🕐 JST)>
we can't make something a requirement if it is entirely out of our control
MDN is a github repo
05:25
<nicolo-ribaudo>
and each proposal (now) has a meta that documentation watches
From what I understood, the problem is that it's still Firefox-centric. The ideal time to write docs is after that one implementation ships, and before stage 4. From Firefox's issue tracker, you can either get notified when a proposal reaches stage 3 (too early) or when Firerfox implements it (sometimes too late)
05:26
<yulia>
yep, thats true -- was giving some insight about how the process currently works
05:26
<yulia>
we don't actually tell MDN what to write about, and sometimes they write docs before we ship
05:26
<nicolo-ribaudo>
we can't make something a requirement if it is entirely out of our control
True, their recommendation was actually "require that MDN folks have been notified in advance so that they can make sure to prioritise docs"
05:26
<yulia>
they just watch that issue for upcoming topics
05:26
<yulia>
and we have a meta issue for every proposal at stage 3
05:27
<Michael Ficarra>
True, their recommendation was actually "require that MDN folks have been notified in advance so that they can make sure to prioritise docs"
that's fine by me
05:27
<Chris de Almeida>
while I very much want the docs, I am wary of asking more from proposal champions
05:27
<Michael Ficarra>
MDN is a github repo
I am not an expert in the kinds of things that go into writing user-facing documentation
05:28
<nicolo-ribaudo>
while I very much want the docs, I am wary of asking more from proposal champions
They are not asking us to write docs for them (they are technical writers, it's their job!), just to coordinate better
05:28
<Chris de Almeida>
then it doesn't seem like we can require it
05:28
<nicolo-ribaudo>
Well, HTML for example requires that a docs issue must have been opened before merging the change to the spec
05:29
<yulia>
nicolo-ribaudo: which proposals were missed?
05:29
<Michael Ficarra>
yeah I am fine with asking for an issue to be opened or something akin to that
05:30
<Chris de Almeida>
I am fine with the spirit of it, but I hesitate to codify something 3rd party. but if everyone supports it, I won't get in the way
05:30
<nicolo-ribaudo>
nicolo-ribaudo: which proposals were missed?
Recent examples were symbols as weakmap keys and regexp v mode
05:31
<ptomato>
a coworker and I tried to get ahead of the game with Temporal docs for MDN, back when the proposal went to stage 3. it was a frustrating experience because it was blocked for months on getting a Temporal key into the browser compatibility data repo. I had to conclude that the process wasn't really navigable for outsiders. I don't think we can really require it from our side
05:32
<shu>

waldemar: it's not a full pre-parse phase, it's an eager, regular parse phase. pre-parse = parse for syntax errors, do not generate an AST, do not generate bytecode. regular parse = parse, generate an AST, and bytecode.

since pre-parse is default, it turns out slower for a function that's immediately executed to first pre-parse, then to do a full parse. so this is a hint to say "skip that initial pre-parse, because it's gonna be called immediately"

05:33
<shu>
ah, i completely anticipated the wrong question waldemar, apologies
05:45
<nicolo-ribaudo>
⚠️ Remember that this room is public
05:45
<Chris de Almeida>
yes, do not discuss topic here, or anywhere in matrix for that matter
05:46
<Aki>
/set mode +m
05:47
<Rob Palmer>
Due to explicit lazy-loading, our system lends itself to loading code that is highly likely to be executed. Prior to us rolling out client-side code-caching, we found maximal PIFEs sped up app load times. The marginal cost of the unnecessary preparse was costing a lot (>100ms.). I believe the need for those legacy-style hints went away after we switch to bytecode caching with high hit rates (90%+ of modules hit the cache). But for cache misses, I would expect the source hints to still be valuable.
05:48
<rkirsling>
what is the point of void there (this question should be separate from the topic, I assume)
05:53
<hax (HE Shi-Jun)>
what is the point of void there (this question should be separate from the topic, I assume)
just ignore the value?
05:54
<rkirsling>
yeah, I guess so
05:54
<hax (HE Shi-Jun)>
what's the behavior ofseal and freeze on resizeable typed array?
05:54
<hax (HE Shi-Jun)>
I remember freeze just throw?
05:55
<hax (HE Shi-Jun)>
so another option is just throw?
05:57
<Chris de Almeida>
🤫
06:29
<Ashley Claymore>
what is the point of void there (this question should be separate from the topic, I assume)
how topical!
06:34
<rkirsling>
hah!
06:34
<rkirsling>
I just couldn't wait ;)
06:35
<rkirsling>
that'd be an awfully large needs-consensus PR methinks
06:36
<Michael Ficarra>
yeah there's no way that could be done as a needs-consensus PR
06:47
<littledan>
it’s not just linters: if you use VS Code, then you will immediately get things marked if they are unused, kinda like a soft error, and with void, you wouldn’t get it marked. I guess tools could learn a convention like _a and do the same.
06:49
<ljharb>
TS also warns on it unless you follow their nonconfigurable convention
06:50
<nicolo-ribaudo>
All of this shows that we made a mistake in the design of using
06:53
<Jack Works>
this is what pattern matching explained why a void is better
06:54
<hax (HE Shi-Jun)>
Though I guess pattern matching could still make _ special case, make it work like Any pattern.
06:54
<hax (HE Shi-Jun)>
All of this shows that we made a mistake in the design of using
What mistake?
06:54
<Jack Works>

for pattern mathcing currently the following works:

when { x: let _ }: // unused _
when { let x }:  // unused x
when has 'x': // test 'x' in subject (a possibly extension)
06:56
<hax (HE Shi-Jun)>
how when has 'x' work for multiple property?
06:56
<Jack Works>
when has 'x' and has 'y'
06:56
<nicolo-ribaudo>
What mistake?
Using using ID = ... instead of something else (example: using { expression })
06:57
<hax (HE Shi-Jun)>
when has 'x' and has 'y'
Not bad 🙄
06:57
<Jack Works>

or

when { let something } and has 'x': ...
06:58
<hax (HE Shi-Jun)>
Using using ID = ... instead of something else (example: using { expression })
I remember ron mentioned some reasons why not do that , but I can't recall
06:58
<Jack Works>
note { let x } and has 'x' are both additional feature and may not exist in the final version. see https://tc39.es/proposal-pattern-matching/ for details
06:59
<Jack Works>
when [let _, let _2, let _3, let x]: ...
when [void, void, void, let x]: ...
when [,,, let x]: ...
06:59
<hax (HE Shi-Jun)>
Consider pattern match is new syntax, we always can make when { x: _ } work, (at least work if there is no _ binding exist?)
06:59
<littledan>
This proposal is good. I hope we do it.
07:00
<littledan>
I wonder how browsers feel about this, if they want it to be a desugaring
07:00
<ljharb>
Consider pattern match is new syntax, we always can make when { x: _ } work, (at least work if there is no _ binding exist?)
it would be very bad if it sometimes was a binding and sometimes was a discard
07:00
<Jack Works>
Consider pattern match is new syntax, we always can make when { x: _ } work, (at least work if there is no _ binding exist?)
this is one of the possible solution what suggested in this discarding proposal
07:01
<hax (HE Shi-Jun)>
it would be very bad if it sometimes was a binding and sometimes was a discard
I agree, but _ is special case. (it's already special in practice)
07:01
<ljharb>
the contention on the discard bindings proposal is that some think it is, and some think it's not. i think it's not. some people definitely treat it as if it's special, is all.
07:01
<Justin Ridgewell>
Ron mentioned that when { x: y } => … means that it’s matching that the object has the structure {x} and x’s current value is equal to y. Rust doesn’t have that, it only matches structure and extracts values, you use an additional when { x } if (x === y) => … to specify an exact value.
07:02
<danielrosenwasser>
Ron mentioned that when { x: y } => … means that it’s matching that the object has the structure {x} and x’s current value is equal to y. Rust doesn’t have that, it only matches structure and extracts values, you use an additional when { x } if (x === y) => … to specify an exact value.

FWIW

https://x.com/pcwalton/status/1359970388839550976
https://x.com/UINT_MIN/status/1359977668460957696

07:02
<littledan>
I just haven’t seen the _otherString pattern in enough cases where it felt like something someone actually wanted to write
07:02
<rbuckton>
If JS didn't have two popular libraries regularly aliased as _, I'd say sure let's special case it. but unfortunately, we do.
07:02
<shu>
I wonder how browsers feel about this, if they want it to be a desugaring
for me personally the complexity is pretty low unless i'm missing the parser work needed to resolve ambiguities
07:02
<shu>
personally i think it is not necessary at all but i won't fight against it
07:03
<shu>
happy to take devs at their word here
07:03
<yulia>
yeah this looks pretty minor to me as well
07:03
<hax (HE Shi-Jun)>
If JS didn't have two popular libraries regularly aliased as _, I'd say sure let's special case it. but unfortunately, we do.
but I think most people do not use _ as the alias in recent years I guess?
07:03
<littledan>
for me personally the complexity is pretty low unless i'm missing the parser work needed to resolve ambiguities
This is really helpful to understand: it is a cost/benefit tradeoff, and here the cost is low
07:03
<ljharb>
but I think most people do not use _ as the alias in recent years I guess?
tons of people still do for underscore/lodash, at least
07:03
<shu>
it's always a cost/benefit tradeoff
07:03
<shu>
like if there's no cost ot make devs happier, why wouldn't we?
07:03
<Justin Ridgewell>

FWIW

https://x.com/pcwalton/status/1359970388839550976
https://x.com/UINT_MIN/status/1359977668460957696

I’m missing context, what are they trying to say?
07:03
<rbuckton>
Regarding Rust pattern matching. Rust knows statically whether a RHS in a pattern is an existing binding or a new binding, JS does not. Any code could introduce a new global that could change the meaning of something like match (value) { { x: x, y: y }: ... }
07:04
<littledan>
Why would it make devs happier if they are all using tools?
07:04
<keith_miller>
Well I hate devs personally :P
07:04
<shu>
well i do also respect the haters
07:04
<rbuckton>
Because JS cannot determine this statically, the pattern matching proposal says an identifier in a pattern is always a reference. If you intend it to be a declaration, you must use let/const/var to declare it.
07:05
<Justin Ridgewell>
Regarding Rust pattern matching. Rust knows statically whether a RHS in a pattern is an existing binding or a new binding, JS does not. Any code could introduce a new global that could change the meaning of something like match (value) { { x: x, y: y }: ... }
Because your patten matching means something different than Rust's.
07:05
<rbuckton>

So you would write

match (value) {
  { x: let x, y: let y }: doSomething(x, y),
}
07:05
<hax (HE Shi-Jun)>
tons of people still do for underscore/lodash, at least
Not sure about it. I think people use _ or $ in no module era (before commonjs), it seems become less and less after we adopt modules, especially esm.
07:06
<danielrosenwasser>
The context is that you have a choice in pattern matching on whether a bare identifier denotes a new binding, or an existing binding to be checked against
07:06
<nicolo-ribaudo>
Regarding Rust pattern matching. Rust knows statically whether a RHS in a pattern is an existing binding or a new binding, JS does not. Any code could introduce a new global that could change the meaning of something like match (value) { { x: x, y: y }: ... }

In rust it's just syntax-based, right? i.e. in this case you are introducing a new binding x:

fn main() {
    let x = 42;
    let y = Some(3);
    match y {
        Some(x) => println!("Some"),
        _ => println!("Other")
    }
}
07:06
<rbuckton>
Because your patten matching means something different than Rust's.
It only really differs in how bindings are declared, by necessity.
07:06
<ljharb>
Not sure about it. I think people use _ or $ in no module era (before commonjs), it seems become less and less after we adopt modules, especially esm.
123K results on github alone https://github.com/search?q=%22import+*+as+_+from+%27lodash%27%22&type=code
07:07
<Justin Ridgewell>

In rust it's just syntax-based, right? i.e. in this case you are introducing a new binding x:

fn main() {
    let x = 42;
    let y = Some(3);
    match y {
        Some(x) => println!("Some"),
        _ => println!("Other")
    }
}
You are always introducing a new binding x, you are never matching a value y === Some(x)
07:08
<Justin Ridgewell>
To match a value, you use an if check on the case
07:08
<rbuckton>

In rust it's just syntax-based, right? i.e. in this case you are introducing a new binding x:

fn main() {
    let x = 42;
    let y = Some(3);
    match y {
        Some(x) => println!("Some"),
        _ => println!("Other")
    }
}
Some(x): may seem obvious, but what about just { x: Number }. Should I be declaring a new variable named Number or referencing the Number constructor?
07:08
<Ashley Claymore>
yeah this looks pretty minor to me as well
would there be any (tiny) advantage to engines, to immediately know the binding is a discard as opposed to discovering that only once the parse finished? Potentially keeping the set of names to track smaller
07:08
<hax (HE Shi-Jun)>
123K results on github alone https://github.com/search?q=%22import+*+as+_+from+%27lodash%27%22&type=code
ok, let me write a bot to send PRs 😂
07:08
<ljharb>
lol, careful github doesn't autoban you :-p
07:09
<rbuckton>
would there be any (tiny) advantage to engines, to immediately know the binding is a discard as opposed to discovering that only once the parse finished? Potentially keeping the set of names to track smaller
Do implementations need to declare the number of locals within a function? That increases the memory size of a function at runtime.
07:09
<yulia>
would there be any (tiny) advantage to engines, to immediately know the binding is a discard as opposed to discovering that only once the parse finished? Potentially keeping the set of names to track smaller
erm. maybe i am sleep deprived, how would we do this before parse?
07:09
<yulia>
yep (we do anyway)
07:10
<rbuckton>
So yes, there is probably a tiny benefit to implementations since they don't need to hold onto a local for those variables.
07:10
<rbuckton>
The spec text also skips adding a binding.
07:10
<Ashley Claymore>
I was thinking 'during the parse', rather than 'before the parse'
07:11
<Justin Ridgewell>
Can anyone on the call hear the room?
07:11
<yulia>
thinking
07:11
<danielrosenwasser>
For a using you still need to basically keep the "discarded" value around until the end of the scope, so even if it's not identifiable by a variable name, some work needs to happen.
07:12
<danielrosenwasser>
(right?)
07:12
<nicolo-ribaudo>
shu It's what the user reads in the callback of the event listener
07:12
<rbuckton>
For a using you still need to basically keep the "discarded" value around until the end of the scope, so even if it's not identifiable by a variable name, some work needs to happen.
For a using, there are two memory locations that hold the value. One is in the environment record for later disposal, the other is in the variable binding itself.
07:12
<yulia>
you would still do the whole parse before constructing the necessary stuff for the script's representation. so this wouldn't be a benefit during parse i think... i don't recall the details of the proposal right now -- if a const binding is void is that name disallowed in the function? im not sure it would help, that might just be more stuff to track and then throw errors on
07:13
<shu>
shu It's what the user reads in the callback of the event listener
so "nullable" on that slide means "could be null in the case there was no JS code that triggered the dispatch"
07:13
<yulia>
you'd still have to do the full parse. but, we could discard it as part of our count of things that are local to the function, thats true
07:13
<yulia>
i don't think its a huge improvement
07:13
<littledan>
Is there time for a timebox extension? This proposal deserves a bit of discussion
07:14
<Chris de Almeida>
yes
07:14
<Michael Ficarra>
yeah it should've been given a bigger timebox to begin with
07:14
<nicolo-ribaudo>

x.run("registration", () => {
  addEventListener("foo", () => { x.get() })
});

x.run("dispatch", () => {
dispatchEvent("foo")
})
07:14
<nicolo-ribaudo>
^ example of the two contexts
07:15
<nicolo-ribaudo>
If the dispatch happens from user interaction, than that's null
07:15
<nicolo-ribaudo>
If it's from JS code, there is something
07:15
<shu>
uh i forget the signature for run()
07:16
<Justin Ridgewell>
uh i forget the signature for run()
The first param is the new internal value of the async var, the second is a callback that is invoked immedaitely. When the callback returns, the internal value is reset to the previous value.
07:18
<yulia>
hello everyone, I am your ersatz chair for the next 5 min
07:21
<yulia>
chair relinquished
07:21
<jkup>
you had a good run
07:31
<nicolo-ribaudo>
For whoever can speak to Mark: I can come to TG3 next week if we don't get to your TCQ topic
07:32
<Aki>
ljharb: ☝️
07:34
<jkup>
Did the call drop remotely?
07:35
<nicolo-ribaudo>
It was you in the room having problems
07:35
<nicolo-ribaudo>
I could hear Mark well
07:47
<Aki>
while we're between agenda items: i'm looking for an example of a small proposal focussed on developer ergonomics that went through the stage process pretty smoothly in the past few years
07:48
<rkirsling>
I think Promise.try flew through pretty quick
07:48
<Rob Palmer>
Promise.withResolvers
07:49
<nicolo-ribaudo>
I think Promise.try flew through pretty quick
8 years according to the champion
07:49
<rkirsling>
oops
07:50
<jkup>
8 years according to the champion
8 smooth years though
07:50
<rkirsling>
totally missed that part somehow lol
07:50
<nicolo-ribaudo>
To be fair it has been at stage 1 for 7 of those 8 years
07:51
<ljharb>
stage 2 to 4 was indeed smooth
07:51
<ljharb>
1 to 2 was a beast tho
07:55
<rkirsling>
"gently unsure" is such a great phrasing
08:00
<shu>
in general, for API features that are purely motivated by convenience, i'd like us to take a bigger scope view and do some prioritization
08:00
<shu>
michael has followed a program of 'make iterators more helpful', which i personally agree is well motivated
08:01
<shu>
but i do regularly hear the deficiency of the JS stdlib
08:02
<shu>
to take a personal pet peeve: we don't have some pretty basic data structures, like Queue, and people are using arrays to poorly emulate. how does something like that weigh against 'make iterators even more helpful'
08:02
<yulia>
yeah, thats a good question
08:03
<rkirsling>
is that a true statement? I have always viewed the push/pop/shift/unshift methods to imply that arrays simply are stacks or queues or whatever
08:04
<ljharb>
arrays are sadly queues, and stacks, and lists, all at once
08:04
<shu>
there are crimes being done in the engine to support the expected algorithmic complexity of queue vs stack vs random-access arrays
08:04
<shu>
that's what i meant by "poorly emulate" above
08:04
<Michael Ficarra>
also MultiMaps!
08:05
<shu>
yes, you can use them as such, but we'd also like have data structures fit for purpose
08:05
<Michael Ficarra>
please someone give us MultiMaps
08:05
<shu>
it might be a bit late
08:05
<rkirsling>
relative to which it would seem needlessly Java-like to demand a Queue or Stack class... but then that could also have been an argument against Map (wrt Object), so
08:05
<nicolo-ribaudo>
please someone give us MultiMaps
Is it the compositeKey proposal Ashley is working on?
08:05
<rkirsling>
maybe the times have simply changed on me
08:06
<nicolo-ribaudo>
Oh no it's something else
08:08
<nicolo-ribaudo>
I was distracted
08:08
<nicolo-ribaudo>
What's happening now? Are done?
08:08
<Chengzhong Wu>
Restricting subclassing support in built-in methods: Telemetry Results (Firefox) Update
08:08
<shu>
no, we're doing yulia's update on killing species
08:09
<Justin Ridgewell>
Yulia is presenting subclassing
08:09
<shu>
i should've named the proposal Extinction
08:09
<nicolo-ribaudo>
Oh ok thanks to who spoke
08:09
<Chengzhong Wu>
20min
08:13
<rbuckton>
I use Symbol.species for Array subclasses when I don't want .map/.filter to produce an instance of the subclass because the constructor parameters differ.
08:14
<rbuckton>
Without it I have to override map, filter, slice, et al and provide my own implementation.
08:14
<shu>
that seems better
08:14
<rbuckton>
Not really.
08:15
<Ashley Claymore>
if we hadn't of done Symbol.species, wouldn't you have gotten your desired behaviour by default?
08:16
<rbuckton>
It's not that I need Symbol.species. It's that I need some way to tell the superclass to just produce Array and not my subclass.
08:17
<rbuckton>
Yes, if we didn't have symbol.species and the default behavior was not to try to produce an instance from the current constructor, that would have been acceptable.
08:18
<Ashley Claymore>
This is what we do in the new Array methods
08:18
<littledan>
XRegExp was a big source of type 4 stuff
08:18
<rbuckton>
But if you unship Symbol.species you need to choose whether to only return Array instances or to only return this.constructor instances. The question is who are you breaking by choosing one or the other.
08:19
<nicolo-ribaudo>
I feel that "Yeah" very hard
08:19
<Ashley Claymore>
class MyArray extends Array {}
new MyArray().toSorted() instanceof MyArray; // false
08:21
<littledan>
But if you unship Symbol.species you need to choose whether to only return Array instances or to only return this.constructor instances. The question is who are you breaking by choosing one or the other.
Yeah I suspect that Array and maybe Promise are more real than RegExp—more plausible to have not-just-polyfill usages
08:21
<rbuckton>
Essentially, I only use Symbol.species to work around the existence of Symbol.species.
08:22
<nicolo-ribaudo>
Can we use AI to filter those cases
08:22
<nicolo-ribaudo>
Google is already shipping it in the browser
08:22
<nicolo-ribaudo>
It can run in the background and check all the loaded pages :)
08:22
<rbuckton>
I have no intuition as to how many people are subclassing Array and it actually matters that .map() et al produce a subclass. These implementations wouldn't be overriding Symbol.species, just using the default behavior.
08:27
<Justin Ridgewell>
Yah, Type II Array uses are probably genuine
08:27
<Justin Ridgewell>
It’s just too simple for a web dev to have written
08:29
<rbuckton>

Historically we've had two opposing positions on subclassing and overriding methods:

  1. Overriding methods is bad because anyone can call superclass.prototype.method.call(subclassinstance) and ignore any behavior imposed by the subclass. The suggested solution was to somehow bake this information into the instance. Using Symbol.species falls under this category
  2. Don't use Symbol.species and instead override methods.
08:30
<rbuckton>
For example, (1) was the approach considered for things like customizing keying for Map/Set.
08:31
<rbuckton>
1 and 2 aren't polar opposites, but they do cross purposes wrt Symbol.species and how/when to handle subclass construction for results.
08:33
<rbuckton>
A third approach is wrapping, but you cant wrap an Array due to its exotic nature.
08:34
<rbuckton>
(at least, not without a Proxy, which is hard to get right and slow)
08:50
<hax (HE Shi-Jun)>
Symbol.species provide a hook which just like protected member in OOP. Compare to other protocol I don't think it's inevitable evil. My question is why it's a static property? If it's a instance property, would it be better for the engines?
09:55
<Anthony Bullard>

I think the thing missed in this discussion is that in languages that do a ton of this, and where it’s fast (zero cost-ish or better) the extraction requires no function call or hidden user code. It’s literally the same lookups and comparisons you can see in the patterns and possibly some branch caching.

If we got rid of Extractors with custom user code, 90% of concerns over hidden perf costs go away

11:56
<Duncan MacGregor>
Did the "Next meeting host and logistics" agenda item get cut, or was it simply not minuted? I don't see it in the day 1 notes.
23:12
<Michael Ficarra>
Did the "Next meeting host and logistics" agenda item get cut, or was it simply not minuted? I don't see it in the day 1 notes.
It was briefly mentioned.