00:04
<shu>
ljharb: so about https://github.com/tc39/proposal-atomics-wait-async/issues/28
00:05
<shu>
ljharb: jridgewell has now convinced me in the status quo, namely that validation errors *should* return early; trigger finger was too quick on making the agenda PR. i imagine no issues with deleting the item without a PR
00:05
<ljharb>
no, there'd be no issues with that, but i'm not sure if that's true
00:06
<ljharb>
axel's article is fine advice for userland, and something i tend to follow as well, but the explicit decision (after that was posted) for `async function` was that it's zalgo to have sync errors from an async function
00:06
<ljharb>
eg, it should be impossible for an `async function` to synchronously throw under any circumstances
00:07
<ljharb>
also `Promise.all()` rejects, it does not throw
00:07
<ljharb>
that's a pretty clear precedent for "all errors of any kind only ever reject"
00:08
<shu>
justin has graciously provided counterexamples that it does
00:08
<shu>
Promise.all.call({}, [1, 2, 3]) // => Uncaught TypeError: #<Object> is not a constructor
00:08
<ljharb>
hm
00:09
<ljharb>
it's inconsistent with the ES6 ones, true
00:09
<shu>
you may argue that it's somehow a categorically different kind of validation error
00:09
<ljharb>
i'd say it kind of is
00:09
<shu>
i'd say i disagree :)
00:09
<ljharb>
in this case tho, it's because it can't know what kind of promise to create without a constructor
00:11
<shu>
OTOH webidl agrees with you and disagrees with me
00:11
<ljharb>
it seems strange if `waitAsync` can't be implemented with an `async function`.
00:15
<shu>
i don't understand what that means
00:15
<shu>
like you can't self-host it with an async function?
00:17
<shu>
(why is that weird? i'm a novice at actual software engineering with async features)
00:17
<ljharb>
yeah that's what i meant
00:17
<ljharb>
i think it's weird if a promise-returning function can't be self-hosted with the syntax for a promise-returning function.
00:18
<shu>
i see
00:18
<shu>
right, ok
00:20
<shu>
ljharb: okay, i need some time to digest. i don't really have skin in the game, so i'm kind of see-sawing
00:20
<ljharb>
fair enough
00:20
<shu>
i can see where the promise subclassing error is just categorically different
00:20
<shu>
and that since webidl as a matter of course converts all exceptions to rejected promises
00:20
<shu>
alignment with that seems most useful
00:25
<jridgewell>
I think following web API's practices would be good
00:25
<jridgewell>
My position was a weakly held "promise returning functions _could_ sync throw"
00:26
<jridgewell>
But looking through every example I could think of, the only ones I could find were the Promise constructor's static methods
00:26
<jridgewell>
I'd accept that's a 1-off exception :drum
00:26
<jridgewell>
I'd accept that's a 1-off exception :drum:
00:26
<jridgewell>
Agh, my pun is ruined.
00:29
<devsnek>
id say the errors should be async
00:30
<devsnek>
they're validating the arguments right
00:30
<devsnek>
I can maybe just barely see the argument for sync throw with the receiver being invalid
00:30
<devsnek>
but I'd rather that was async too
00:33
<Bakkot>
yeah, my impression was definitely that the webidl convention is pretty universal in modern js
00:34
<Bakkot>
it's also what node does, at least for `fs.promises`
00:35
<Bakkot>
if we were to deviate here I would expect the justification to be something specific to atomics, but I don't think there is such a reason
00:38
<Bakkot>
btw, in case anyone didn't get the "zalgo" reference, it's https://blog.izs.me/2013/08/designing-apis-for-asynchrony
00:40
<devsnek>
Facebook apparently replaces promises with fake promises that can resolve without waiting a tick
01:06
<shu>
great, thanks for all the datapoints
01:07
<shu>
will turn the errors async and ask for consensus for it
01:07
<shu>
incidentally how do folks feel about a Promise introspection API for the power uses that want to do something synchronously with Promises?
01:08
<shu>
domenic pointed me to a previous iteration: https://github.com/jamiebuilds/proposal-promise-prototype-inspect
01:08
<devsnek>
so much nope
01:09
<devsnek>
the demos are like literally the antithesis of the design of promises
01:10
<shu>
that promises being non-introspectable is central to its design?
01:10
<shu>
i missed that
01:10
<devsnek>
zalgo again
01:11
<ljharb>
yes, it shouldn’t be possible to observe the result synchronously
01:11
<devsnek>
if you want to do that just don't use promises
01:11
<shu>
so it's a slippery slope argument, that it'll enable that use case?
01:11
<devsnek>
is there any other use case
01:12
<devsnek>
the repo seems entirely focused on "the promise is fulfilled but you don't want to wait a tick to get the result"
01:12
<shu>
well, the current design of Atomics.waitAsync misses an optimization opportunity because mixing sync and async is a no go, and introspecting promises synchronously is no go
01:12
<shu>
if there were a way to do the latter, if that's somehow more palatable than the former, that'd re-enable that optimization opportunity
01:12
<devsnek>
optimization where you don't have to wait a tick?
01:13
<shu>
right
01:13
<Bakkot>
what's the optimization?
01:13
<devsnek>
right the design of promises is that you always have to wait a tick
01:13
<shu>
https://github.com/tc39/proposal-atomics-wait-async/blob/master/SYNC-RESOLVE.md
01:13
<shu>
basically, the point of the wait API is a building block for efficient mutexes in userland
01:14
<devsnek>
waitNonblocking seems fine to me
01:14
<devsnek>
like i said, the solution is to not use promises
01:14
<shu>
it... can't not use promises
01:14
<Bakkot>
devsnek: waitNonBlocking returns a promise
01:14
<Bakkot>
that is the whole point of it
01:14
<devsnek>
oh
01:14
<shu>
also the name is out of date, it's waitAsync now sorry
01:14
<devsnek>
well
01:14
<shu>
but anyway the point of the API is a conditional wait
01:14
<devsnek>
Atomics.tryWait
01:14
<devsnek>
or something
01:14
<shu>
i'm not sure what that means
01:15
<devsnek>
it exits out if it would block
01:15
<devsnek>
and then you can go do other things or call the waitAsync function
01:15
<shu>
ah but whether an agent can block or not is a static property of the agent. the main thread can't block
01:15
<shu>
it's not that the user is choosing to block or to get a promise, it's that there's no choice
01:16
<Bakkot>
can you not just do Atomics.read, and then a compare, and then Atomics.waitAsync?
01:16
<devsnek>
it somehow doesn't always return a promise
01:16
<shu>
Bakkot: yep, that's the workaround at the end
01:16
<Bakkot>
oh, cool
01:16
<Bakkot>
that seems fine to me in honesty; I would not expect that to be a significant performance difference
01:16
<shu>
Bakkot: that's probably just fine in practice 99% of the time. there's a TOTCTOU problem so you might get really surprising degraded mutex performance once on a blue moon
01:16
<shu>
Bakkot: agreed, i don't plan to change it, just got to thinking about it again
01:17
<shu>
the weirdness is mainly that it *feels* weird to have a conditional wait API that's supposed to "fail fast" and signal when you don't need to wait, but for the async version, you can't observe that signal until the microtask checkpoint
01:17
<shu>
at which point, you'd probably already waited a while
01:18
<devsnek>
i guess its worse for the web since it has the big render cycle thing to deal with
01:18
<Bakkot>
the TOTCTOU problem is there for atomics.waitAsync too, yeah? nothing prevents the value from changing out from under you immediately as soon as the promise resolves, before you have had time to read from it?
01:18
<Bakkot>
shu anyway the alternative is, you return either `{ fast: true, value: value }` or `{fast: false, promise: promise }`
01:18
<Bakkot>
and then the user switches on `fast`, and awaits the promise if they got one
01:18
<shu>
Bakkot: ah good point, the problem is there for the entered-the-wait-queue path as well
01:19
<shu>
but i don't think in practice, once a thread is woken
01:19
<shu>
you care about what the value of the futex location is
01:19
<shu>
Bakkot: yeah, that also feels kinda gross, so status quo seems all right for now
01:19
<Bakkot>
once atomics.read has returned the right value, you also don't care, right?
01:20
<shu>
you do, because you want the "the read value is the right one, now enter the wait queue" to be an atomic action
01:20
<shu>
in case there's a lot of contention, for instance
01:20
<Bakkot>
if the value read is the right one, why are you entering the queue?
01:20
<shu>
that's the futex api, you enter the wait queue when *addr == val
01:21
<Bakkot>
wait ok correction. why is the thing you would do after Atomics.waitAsync has fulfilled successfully any different from the thing you would do after Atomics.read has returned the value you were looking for?
01:22
<Bakkot>
(it has been a while since I did low-level multithreading, sorry)
01:22
<devsnek>
because the thing you do next is entering the wait queue
01:22
<devsnek>
which should be atomic
01:22
<devsnek>
oh after it fulfills
01:22
<devsnek>
nvm
01:22
<shu>
Bakkot: the canonical example is something like, you have a tri-state mutex: 0 == unlocked, 1 == locked, 2 == locked and contended
01:22
<shu>
Bakkot: if there's contention, you don't want to immediately block the thread and wait, you want to do that if there's contention
01:23
<Bakkot>
that last message has a typo presumably, both of your branches are "there's contention"
01:23
<shu>
oops, if there's *no* contention you don't want to immediately block
01:24
<devsnek>
sounds like we need futures
01:24
<shu>
Bakkot: so if the state is != 0, you wait only if the state is already == 2, or successfully compxchg the state from 1 to 2
01:24
<shu>
otherwise, sometimes it gets "fast unlocked" in the interim and you just acquire the lock
01:25
<shu>
so the futex wait call is on that state == 2
01:27
<shu>
does the TOTCTOU make sense now?
01:27
<Bakkot>
still thinking through it
01:27
<shu>
Bakkot: https://eli.thegreenplace.net/2018/basics-of-futexes/
01:27
<shu>
search for "simple mutex"
01:27
<shu>
there's a nice commented code snippet
01:30
<Bakkot>
this snippet seems like you could implement it just fine using Atomics.compareExchange + the promise-based Atomics.waitAsync (in place of the sleep)
01:31
<Bakkot>
I guess the point is that you want to return early if it has been unlocked _between_ your compare-exchange and your call to waitAsync?
01:32
<Bakkot>
this being the "fast unlock" thing
01:32
<shu>
right
01:32
<shu>
where the cmoment says "Note that it's not necessary to loop around this syscall"
01:32
<shu>
the loop in JS land with waitAsync is waiting until the microtask checkpoint
01:32
<shu>
which... might be short, might be long, who knows? app dependent
01:35
<Bakkot>
If the syscall was guarded on `if (Atomics.read(atom) != 0) {`, would that not do the thing you want?
01:35
shu
thinks
01:37
<shu>
no, i don't think so, you can imagine some scheduler that executes that line
01:37
<shu>
then basically parks that thread
01:38
<shu>
in the meantime 2 other threads do their locking/unlocking thing, and by the time you resume the first thread, you have 0 again, right?
01:39
<Bakkot>
ah
01:39
<Bakkot>
yeah
01:39
<Bakkot>
ok
01:39
<shu>
i don't think it's possible to fix in userland
01:39
<shu>
but the chances of failure are exceedingly small
01:39
<shu>
and "failure" just means extra long to acquire a lock, not deadlock or anything
01:42
<Bakkot>
yeah
01:43
<Bakkot>
and the case it comes up is specifically where someone has unlocked your futex between the time you looked and the time you went to enter the queue, which is pretty much the immediate next operation you are doing
01:43
<Bakkot>
having the extra microtask tick on the main thread in that case seems ok
01:45
<Bakkot>
this is definitely an Atomics-specific reason to be able to return early sometimes, though; if we wanted to address it I think having your API return sometimes-sync sometimes-promise (ideally wrapped, as in my example above) would be a better fix than introducing a general-purpose promise introspection utility
01:48
<shu>
right
01:48
<shu>
Bakkot: yeah, that sounds very reasonable
01:49
<shu>
need to stew on whether we want to return a wrapped thing
01:49
<shu>
it's kinda unergonomic to not be able to write `await Atomics.waitAsync(...)` but shrug
01:50
<Bakkot>
hmm, yeah
01:50
<Bakkot>
I guess it would be ok for it to not be wrapped, given that await can be passed a non-promise value
01:50
<Bakkot>
I dislike that fact but it is what it is
01:51
<Bakkot>
though, actually, does that `await` not introduce an additional microtask tick in the case that waitAsync returns a promise?
01:52
<devsnek>
it doesn't
01:52
<devsnek>
though it used to
01:52
<Bakkot>
ahh
01:52
<Bakkot>
cool
01:52
<devsnek>
that was the "await optimization" thing the chakracore people brought at some point
01:52
<devsnek>
changed it to just do Promise.resolve(v).then(resume)
01:54
<jridgewell>
The await optimization took it from 3 ticks to 1 tick.
01:54
<jridgewell>
But using `await` always ticks.
01:55
<jridgewell>
The only way a sync-promise would work is if you did `Atomics.waitAsync(...).then(syncStuff)`
02:03
<Bakkot>
shu: I guess I am convinced that a not-wrapped value would be OK. I would maybe prefer `{ fast: true, value: 0 }` or `{ fast: false, value: promise }`, so that you don't have to do the typeof check and can still do the handy `await Atomics.waitAsync().value`.
02:03
<Bakkot>
but, also, I don't think this necessarily needs solving
03:07
<shu>
i think wrapped is the way to go if we wanna solve it, but yeah i agree i’m not sure this needs solving
17:55
<TabAtkins>
Btw, I know I missed this meeting's deadline (not thru laziness! we just came up with the idea too late!), but putting this out there in prep for the next meeting: https://github.com/tabatkins/proposal-item-method
17:57
<devsnek>
hopefully we can dedicate 30-40m to bikeshedding the name
17:58
<Bakkot>
devsnek: fortunately the justification for this one is in many ways tied to the name being exactly `item`
17:58
<Bakkot>
which is quite nice
17:59
<devsnek>
why not at()
17:59
<Bakkot>
read the readme
18:00
<Bakkot>
tldr is "to match the DOM"
18:00
<shu>
i think we've been all right about bikeshedding recently
18:00
<devsnek>
we don't *have* to match the dom though
18:00
<shu>
we don't have to do a lot of things
18:00
<devsnek>
not that i disagree with the motivation
18:01
<Bakkot>
devsnek: we don't have to, but the advantage of matching is not just for consistency but that it allows the DOM array-wrapping apis to avoid some magic
18:04
<devsnek>
i like the proposal
18:04
<devsnek>
just wanted to point out it was technically not imperative
18:04
<Bakkot>
ah, yeah
18:05
<Bakkot>
yeah it's not imperative, just, there's a strong built-in bias towards one particular name, which helps avoid bikeshedding
18:07
<Bakkot>
TabAtkins: fwiw it looks like mootools does _not_ have `.item`; it was the largest offender on fragile builtin overrides pattern, to my knowledge. so that's hopeful.
18:07
<TabAtkins>
Nice.
18:07
<devsnek>
smooshIntoCollectionIndex(n)
18:08
<bradleymeck>
Prototype / Ext also had issues in the past (goes off to check)
18:09
<bradleymeck>
they both do not have that method
18:12
<TabAtkins>
double nice
18:12
<TabAtkins>
re: bikeshedding; I
18:12
<TabAtkins>
lol
18:13
<devsnek>
clearly we need to add a new syntax
18:13
<devsnek>
japanese quotation marks
18:13
<TabAtkins>
re: bikeshedding; I'm one of the people who've desperately wanted negative indexing as well, so *any* name would make me happy on those grounds, but yeah, using exactly .item() would make me even happier
18:13
<devsnek>
there was a proposal from someone at some point for indexing/stepping syntax
18:13
<devsnek>
might've been on the discourse
18:14
<bradleymeck>
API would be simpler / provides benefits that can be backported cheaper
18:14
<devsnek>
yeah i agree
18:17
<TabAtkins>
Bakkot/bradleymeck: thanks, added the info to the proposal
18:19
<TabAtkins>
btw, am I right in my findings that the TypedArray superclass isn't publicly exposed under any name, and so the way to add to all typed arrays' prototypes is indeed `Uint8Array.__proto__.prototype.item = item;`?
18:20
<bradleymeck>
TabAtkins: use Object.setPrototypeOf as Deno/Node don't guarantee __proto__ anymore, but yes
18:21
<bradleymeck>
getPrototypeOf*
18:22
<devsnek>
i wonder if we should have an Iterator.prototype.nth
18:22
<bradleymeck>
-1th
18:22
<devsnek>
probably not since we can't really lower it for perf
18:22
<devsnek>
but its nice in rust
18:23
<TabAtkins>
Anything that implies random access over an iterator is probably bad.
18:24
<TabAtkins>
You want to be more explicit that you're first discarding N-1 values from the iterator, then taking the next one.
18:24
<devsnek>
there was an interesting discussion along that line in the proposal
18:24
<devsnek>
about giving an index in the map/etc functions
18:24
<TabAtkins>
That can def be worthwhile to build into the Iterator protocol under an easy name, it just needs to be clear in its semantics.
18:25
<devsnek>
yeah rn you'd do skip(n).next() i guess
18:25
<TabAtkins>
yeah
18:25
<devsnek>
or skip(n - 1) i guess
18:25
<Bakkot>
seems fine to me
18:25
<TabAtkins>
no, skip(n) is correct
18:25
<Bakkot>
that's plenty explicit
18:25
<devsnek>
if you want the fifth item you'd skip four and take one
18:26
<TabAtkins>
where's the zeroth item?
18:26
<TabAtkins>
you filthy 1-indexer
18:26
<devsnek>
skip(0) is no-op
18:26
<TabAtkins>
exactly
18:26
<devsnek>
its the number of items to skip
18:26
<TabAtkins>
Yes, I know.
18:26
<TabAtkins>
I'm saying that ".nth(0)" is "skip 0 items, get the next one"
18:27
<devsnek>
oh i see what you mean
18:27
<TabAtkins>
because the 5th item is at index 5, and thus has five items before it
18:27
<devsnek>
yeah by fifth item i meant
18:27
<devsnek>
lol
18:28
<devsnek>
*violent agreement*
19:00
<shu>
what if it was named nst
19:03
<TabAtkins>
shu: what if you were named nst
19:03
<TabAtkins>
let's compromise and name is nnd
19:03
<shu>
n2d sgtm
19:17
<rkirsling>
`nnd` -- takes a number but rounds it to the nearest integer ending in a 2 first
19:17
<Bakkot>
proposal to add rounding mode flag to Math.round
19:20
<shu>
oh wow
19:20
<shu>
https://upload.wikimedia.org/wikipedia/commons/8/8a/Comparison_rounding_graphs_SMIL.svg
19:20
<shu>
what a delightful chart
19:21
<Bakkot>
this svg has hover effects
19:21
<Bakkot>
I did not know you could do that
19:22
<shu>
neither did i
19:29
<TabAtkins>
Oh yeah, hover is great
19:29
<TabAtkins>
I don't think *this chart* uses them very well; it's still nearly unreadable when you hover one, but hey
19:34
<shu>
i'm impressed by the number of things and the different colors
19:47
<ljharb>
(node still guarantees __proto__ by default, what flags do is different)
19:48
<ljharb>
TabAtkins: so, aside from the DOM pushing a specific name, what semantics does that push for, specifically around validation errors/exceptions and edge cases?
19:49
<TabAtkins>
That's a good question and I'll figure it out and document it
19:50
<ljharb>
TabAtkins: because i'm only mildly annoyed by the web predetermining the name, but if they predetermine other semantics that don't align with what would actually be conventional for JS, i'm a lot more annoyed
19:50
<TabAtkins>
I highly suspect that the more detailed semantics aren't important to compat here
19:52
<ljharb>
that's ideal :-)
19:54
<shu>
ljharb: so i think a uniform way to do relative indexing for indexable data is the high-order bit; if the web compat bit cost is naming, that sounds pretty win-win to me
19:57
<Bakkot>
TabAtkins: NodeList's item does not support negative indexes
19:58
<Bakkot>
changing that behavior seems like it might be breaking
19:59
<TabAtkins>
I doubt that. We'll see!
19:59
<TabAtkins>
Before this ships we'll probably need to instrument and verify.
19:59
<Bakkot>
if not that would be ideal
20:00
<TabAtkins>
Not hard to measure how many pages pass negative numbers to it right now
20:01
<Bakkot>
great
20:02
<Bakkot>
it is easy to imagine someone doing `i = list.length - 1; do { e = list.item(i); doThing(e); --i; } while (e != null)` or whatever
20:02
<Bakkot>
people write all sorts of crazy loops
20:02
<shu>
what does NodeList's item do now when passed a negative number?
20:02
<Bakkot>
returns `null`
20:03
<Bakkot>
(nb not `undefined`, which is probably also something which would have to change)
20:09
<shu>
ah
21:03
<ljharb>
shu: oh sure, for naming that's why i'm only mildly annoyed
21:03
<ljharb>
(fwiw i do consider the feature useless without support for negative numbers)
21:07
<TabAtkins>
Yeah, DOM stuff always returns null, another Java legacy.
21:08
<TabAtkins>
But since most people test for null with either a `== null` or `!` check, switching to undefined has a good chance of being safe.
21:09
<devsnek>
honestly this seems like arguments to not use `.items`
21:09
<devsnek>
er `.item`
21:10
<TabAtkins>
Unless they prove to be breaking, they're not. And the point of me introducing this is to try and get .item() specifically.
21:10
<TabAtkins>
In the worst case, we just don't upgrade the legacy interfaces.
21:11
<TabAtkins>
And JS is free to do whatever.
21:11
<devsnek>
i mean you'd have to prove no code does `=== null`
21:11
<devsnek>
or yeah we can not make the web use the new behaviour
21:11
<TabAtkins>
No, we just have to have reasonable assurance, and not see reported breakage in dev/beta channels.
21:12
<devsnek>
seems like a lot of trouble for no payoff
21:13
<TabAtkins>
The payoff is I get to quit writing `[...document.querySelectorAll("a")].map(foo)`
21:13
<TabAtkins>
becasue NodeList is a freakin' Array now
21:14
<devsnek>
wait do you actually want to replace NodeList with Array
21:14
<TabAtkins>
(Or rather today I always do a `function findAll(sel) { return [...document.querySelectorAll(sel)];}` at the top of my projects
21:14
<TabAtkins>
No, with ObservableArray, which is a proxy around Array that lets us still intercept get/set/etc.
21:14
<TabAtkins>
Domenic wrote it up and got it into WebIDL just a little bit ago.
21:14
<Bakkot>
I have never seen someone spell that function anything other than `$$`
21:16
<devsnek>
so you want it to be called .item so you don't have to worry about whether you're dealing with an ObservableArray or an Array
21:16
<devsnek>
or something else
21:22
<ljharb>
TabAtkins: you should already be writing `Array.from(document.querySelectorAll('a'), foo)` :-p
21:23
<Bakkot>
god why
21:23
<ljharb>
so you don't create an intermediate array
21:23
<ljharb>
Array.from's mapper function is a godsend, i use it allllll the time
21:23
<TabAtkins>
devsnek: ObservableArray is a proxy over Array, so it's not a matter of "which one you see" - as far as the author is concerned they're getting an Array.
21:23
<Bakkot>
something something premature optimization
21:23
<devsnek>
TabAtkins: well in theory ObservableArray could be a subclass and a proxy
21:24
<TabAtkins>
see the doc for exploration of some of the alternate choices and why i prefer not to have them
21:24
<ljharb>
the iterator protocol is slow ¯\_(ツ)_/¯
21:24
<devsnek>
not just a proxy
21:24
<TabAtkins>
including that one in particular
21:24
<ljharb>
Bakkot: but yeah fair
21:24
<Bakkot>
ljharb for arrays? is it really?
21:24
<ljharb>
Bakkot: NodeList isn't an array
21:24
<TabAtkins>
yah it's fakey fake fake
21:24
<Bakkot>
s/arrays/nodelists/
21:24
<ljharb>
Bakkot: i'm sure it's optimized for the common use cases in modern engines, the ones where perf matters the least
21:25
<devsnek>
i wouldn't describe iterators as slow
21:25
<ljharb>
what i also personally like is, not having to rely on `.map` being there
21:25
<devsnek>
but i wouldn't describe them as blistering fast either
21:27
<Bakkot>
ljharb yeah I mean if you observe that you have performance issues I am all for using whatever hacks are necessary to become fast, though usually that means using imperative loops rather than function style
21:27
<Bakkot>
if you have not yet observed that, keep your code readable
21:28
<devsnek>
https://twitter.com/devsnek/status/1243586726976724992
21:31
<rkirsling>
lul
21:32
<ljharb>
Bakkot: i find the array.from example readable personally
21:32
<ljharb>
devsnek: lol
21:32
<devsnek>
readable because you know the signature of Array.from
21:33
<rkirsling>
I didn't know about that second arg
21:33
<devsnek>
TabAtkins: your site is making me dizzy
21:33
<TabAtkins>
the homepage?
21:33
<devsnek>
ya lol
21:33
<TabAtkins>
lol
21:34
<devsnek>
my website is literally text/plain though so i can't really throw shade
21:34
<Bakkot>
ljharb it's fine if you and all future readers of the code can be expected to know about the second argument, but I don't think people should know about it. people should learn about `Array.from()` with one argument, and learn about `map`, and those compose naturally, and then there is no reason to ever learn this third thing (Array.from takes a second argument).
21:35
<Bakkot>
(unless they start getting into microoptimization, of course, but that's gonna need to be backed up with bechmark data for their particular codebase and userbase)
21:35
<ljharb>
i don't think it's unreasonable to expect people to know about something built into the language, that's not esoteric
21:35
<Bakkot>
it should be esoteric
21:35
<Bakkot>
because there is no reason to learn it
21:36
<devsnek>
in theory an engine can combine Array.from().map
21:36
<devsnek>
if the map argument is pure at least
21:36
<ljharb>
if there's no reason to learn it, then why was it shipped
21:36
<Bakkot>
ljharb do you also use the second argument to Array.p.map?
21:36
<ljharb>
devsnek: only if the mapper doesn't access or detect the third argument
21:37
<devsnek>
like i said, if its pure
21:37
<shu>
we ship plenty of things we don't want people to learn, what
21:37
<ljharb>
devsnek: it can be pure even if it accesses it
21:37
<Bakkot>
do you also think it is reasonable to expect them to learn that?
21:37
<ljharb>
Bakkot: no, true enough
21:38
<devsnek>
shu: are you telling me i should've bother learning String.prototype.blink
21:38
<shu>
there's a lot of reason to learn that over second argument to map or from
21:38
<shu>
- street cred
21:38
<TabAtkins>
...what's the second argument to map()
21:38
<ljharb>
ok but the `this` argument to .map was added in 2009, before arrows, and before .bind was common
21:38
<devsnek>
thisArg
21:38
<shu>
- historian cred
21:38
<TabAtkins>
oh, to the map callback
21:38
<TabAtkins>
phew
21:38
<ljharb>
Array.from's mapper arg was added in 2015, along with array spread
21:39
<TabAtkins>
oh no wait i see
21:39
<ljharb>
so what was the rationale for it, if it's not worth learning?
21:39
<devsnek>
TabAtkins: and the callback itself takes (item, index, array)
21:39
<TabAtkins>
i think you mean (element, index, collection) (EIC, still waiting for "haltToken")
21:39
<devsnek>
lol
21:39
<shu>
can confirm that's how brendan himself teaches it
21:39
<devsnek>
`(element, index, collection, signal)`
21:40
<jridgewell>
I don't understand how you could replace these not-an-arrays with `ObservableArray`
21:40
<devsnek>
well if it only breaks less than .0003% of the web or whatever chrome's metric is
21:40
<jridgewell>
We'd have to add every method that the no-an-array has to `ObservableArray` or `Array`?
21:41
<TabAtkins>
jridgewell: There's precisely one.
21:41
<TabAtkins>
.item()
21:41
<jridgewell>
For all the not-an-arrays?
21:41
<TabAtkins>
For a lot of them, at least
21:41
<TabAtkins>
NodeList, StyleSheetList
21:41
<shu>
ljharb: the reason for the mapper function for Array#from was it enables easier array subclassing
21:41
<Bakkot>
(ugh)
21:41
<jridgewell>
Ok.
21:42
<ljharb>
shu: how?
21:42
<rkirsling>
(does this mean `e, i, c, h` is the `s t a b` of JavaScript)
21:42
<jridgewell>
So because `Array` would now conform the the API provided by `NodeList`, it wouldn't matter that we now returned an array...
21:42
<ljharb>
shu: you mean like a subclass doesn't have to override `.map`, just `static from`?
21:42
<jridgewell>
Would there still be the perf cliff?
21:42
<jridgewell>
Eg, a live `NodeList`
21:42
<TabAtkins>
jridgewell: I mean, we still need the proxy for things that are live.
21:42
<shu>
ljharb: https://github.com/tc39/notes/blob/master/meetings/2013-01/jan-30.md#revising-the-array-subclassing-kind-issue
21:43
<ljharb>
shu: lol that says there's a thisArg, but there isn't one
21:43
<ljharb>
but well, now i feel dirty, because one of my favorite parts of the language was created to enable a dumb and inconsistent subclassing model
21:44
<shu>
no i think the lesson here is just all artifacts exist historically
21:44
<ljharb>
fair
21:44
<ljharb>
i retract my implication that existence proves usefulness
21:44
<ljharb>
(but i still find this one useful)
21:45
<TabAtkins>
rkirsling: yes
21:45
<shu>
yeah i think for computers especially after some time the original motivations don't really matter
21:46
<devsnek>
> existence proves usefulness *cries in abstract equality*
21:46
<rkirsling>
TabAtkins: :D
21:46
<TabAtkins>
(i still dont' understand what the `s t a b` mean, either individually or collectively)
21:46
<rkirsling>
it's `s t` and `a b` separately but adjacent
21:46
<rkirsling>
which comically forms a word
21:46
<TabAtkins>
if you try to explain Lenses to me here in IRC i will fight you
21:47
<rkirsling>
I absolutely will not 😂 I actually only understand it at a very high level myself
21:48
<TabAtkins>
yeah i know roughly how to use it, but no clue whatsoever how the abstraction works
21:48
<rkirsling>
i.e. "pure FP decided it wanted in on the property access thing too"
21:48
<TabAtkins>
which annoys me becasue people complain about monads and, like, they're trivial, so i'm not sure if lenses are in the same boat and i haven't hit the insight yet, or they're actually complicated and it's okay
21:49
<shu>
that kind of functional programming is basically that kindergarten game where you need to find the right shaped hole for objects
21:49
<shu>
and then being very proud you stacked several objects together and put it into a novel shaped hole
21:50
<rkirsling>
isn't that kind of the whole idea of naturality in category theory though? :p
21:50
<TabAtkins>
basically yeah
21:51
<devsnek>
someone was really upset that we put in optional chaining instead of a method on properties that returns Option
21:51
<Bakkot>
took me an embarrassingly long time to figure out you were talking about lenses and not pokemon
21:51
<shu>
as much as i dunk on pokemon, pokemon is orders of magnitude more beneficial for society than category theory
21:51
<Bakkot>
( https://bulbapedia.bulbagarden.net/wiki/Same-type_attack_bonus )
21:51
<rkirsling>
shu: ouch
21:52
<rkirsling>
Bakkot: fascinating
21:54
<rkirsling>
that Baez guy is all about applying category theory to save the planet though
21:54
<rkirsling>
here's hoping he finds success 🍷
21:57
<shu>
i think mathematicians are exempt from my hot take
21:57
<rkirsling>
😆
22:32
<Bakkot>
agendas repo seems like it should maybe be read-only for non delegates, if that is a possibility?
22:32
<rkirsling>
+1
22:34
<ljharb>
is it not?
22:35
<Bakkot>
evidently not, no
22:35
<devsnek>
i wasn't able to modify it until i became a delegate
22:35
<devsnek>
i was in the invited experts team before that
22:35
<Bakkot>
oh, sorry, I meant issues as well as the code
22:35
<ljharb>
only for a 24 hour period as a time
22:35
<ljharb>
all issues are open for all on github
22:35
<devsnek>
huh
22:36
<Bakkot>
bleh
22:55
<rkirsling>
:(
23:16
<rkirsling>
Bakkot: you're saying "must implement as specified" is weaker than "must not implement except as specified"? like, the former would still allow for bonus params or something?
23:17
<rkirsling>
er sorry "must not extend except as specified"
23:17
<Bakkot>
rkirsling: yes
23:17
<rkirsling>
I see
23:17
<Bakkot>
and also in the case that 402 is not active it says "the following is used", rather than "the following must be used" or whatever
23:18
<rkirsling>
wait but "is" sounds even more unwavering to me than "must"
23:21
<Bakkot>
sorry, the "it" in that sentence is "BigInt.prototype.toLocaleString"
23:21
<Bakkot>
so, yes, that's my point - the current BigInt.prototype.toLocaleString does not read as a strong of a requirement as the forbidden extensions does
23:24
<rkirsling>
alright
23:25
<rkirsling>
I see
23:26
<rkirsling>
do you agree that it seems like an unintentional omission though?
23:27
<devsnek>
can't we just say "extensions to builtins that are otherwise specified in 402 are forbidden"
23:27
<devsnek>
or something along those lines
23:27
<devsnek>
bikeshedding needed
23:28
<rkirsling>
yeah it might be ideal to not have to keep maintaining the list
23:30
<ljharb>
it seems like a spec bug, but still a normative change
23:30
<ljharb>
i also think it would be good to avoid the list
23:30
<ljharb>
(especially since you can already grep for "402" in the spec to find all those extension points)
23:31
<Bakkot>
rkirsling yes it's definitely accidental; this is just a process point
23:32
<Bakkot>
we can resolve it in two minutes in plenary
23:37
<rkirsling>
cool
23:39
<TabAtkins>
Bakkot: Added a list of upgradeable interfaces, and a (comprehensive afaict) list of the changes from current behavior with an exploration of the possible breakages that could result.
23:39
<TabAtkins>
thanks for the push to do it
23:39
<Bakkot>
nice!