09:54
<littledan>
I don't get it; do we want to encourage people to silently swallow and ignore errors?
10:47
<Chengzhong Wu>
I believe the motivation is to avoid using a big try-catch scope to catch errors for multiple statements. Rather, it encourages to handle error for each function call that may throw, comparing to the "typed" catch blocks. Such syntax also presents in languages like Swift (https://docs.swift.org/swift-book/documentation/the-swift-programming-language/errorhandling/#Converting-Errors-to-Optional-Values). However, Swift has a stronger enforcement that the error can not be easily discarded with this syntax.
11:01
<littledan>
yeah I guess this depends whether we're talking about the [error, value] version or the just-return-undefined-in-case-of-error version
16:08
<Michael Ficarra>
Aki: In the CSSWG we make it a requirement that any slideshows be saved as PDF and sent to www-archive@w3.org, then have its location in the minutes. We should probably adopt a similar policy for slides in tc39 since we're even more slideshow-dependent here.
When our company was acquired and we had to move from GSuite to M365 (don't get me started), they disabled all our accounts and broke all of the links for years of my presentations to TC39. I had to escalate to very high levels to get them to turn my account back on temporarily so I could download PDFs and put them in the agendas repo. A policy to just do this at the end of each meeting would be so helpful.
16:55
<Ashley Claymore>
for the try ... expression. Maybe any non-object exception would get put into a new Error object with the value as the cause? To avoid having to distinguish between code that threw or returned undefined
16:56
<Ashley Claymore>
or maybe that ship has sailed as this isn't a thing that catch blocks do
16:56
<Aki>
So, for future reference, Patrick C does gather and download all the slides and throw them in a zip file after every meeting. They are in the Ecma_Documents/TC39/[year] folder of the ecma file server. But I agree it would be really, really nice if everyone just put them with the agenda.
16:57
<nicolo-ribaudo>
for the try ... expression. Maybe any non-object exception would get put into a new Error object with the value as the cause? To avoid having to distinguish between code that threw or returned undefined
One of the alternatives that the proposal has is three values: a boolean to tell whether it errored, the error, and the return value
16:58
<Ashley Claymore>
right, this is an alternative to needing to return 3 args
16:58
<Ashley Claymore>
can 'just' check if err is truthy
17:00
<Ashley Claymore>
throwing falsey values is ?never? a good idea in the first place?
17:43
<eemeli>
I find it rather hard to justify that [ok, error, value] is needed just because error might be falsy. If your error might be falsy, that's something you ought to fix in your own code rather than having the spec make allowances for it.
17:48
<kriskowal>
There’s also language precedent in Promise.allSettled
17:49
<kriskowal>
That is, precedent for a taggèd union of {status, value, reason}
18:04
<rbuckton>
I find it rather hard to justify that [ok, error, value] is needed just because error might be falsy. If your error might be falsy, that's something you ought to fix in your own code rather than having the spec make allowances for it.
I disagree. It doesn't matter if it's a bad idea to throw a falsy value, it's possible to throw a falsy value so it must be possible to differentiate the result. Otherwise the feature is unreliable in corner cases and developers could make poor assumptions about whether the code actually executed successfully.
using allows for this case as well.
18:12
<eemeli>
Given that a try-expression would be constructing a Result, why not fix this the way that Ashley Claymore suggests above? As in, always wrap a non-Error value in an Error with a cause. As a developer, I'd find it much more useful to be able to rely on result.error always being either undefined, or an Error.
18:15
<rbuckton>
That seems like wasteful allocation of an extra object, along with collecting a stack trace, if a user writes a try foo() in a tight loop and ignores the result
18:15
<rbuckton>
It's convenient, yes, but may not be performant.
18:16
<Michael Ficarra>
Warning: in reviewing the notes, I saw an unknown speaker attributed as ABC. Note takers, please don't do this. Use ??? or something.
18:17
<eemeli>
Is an exception-based control flow ever going to be performant in JS? If performance at this level is a concern, shouldn't the user code never be throwing anything in the first place?
18:22
<Michael Ficarra>
Warning: in reviewing the notes, I saw an unknown speaker attributed as ABC. Note takers, please don't do this. Use ??? or something.
Also some quotes were misattributed to me and I've corrected them with who I think said them using KG?: notation. So if you're searching for quotes attributed to you, you may want to look for that form as well.
18:27
<eemeli>
Having said that, now I'm not actually sure if throwing and catching exceptions are in fact as performant as any other style of control flow in JS, or if they introduce a penalty. I might be remembering things from 10-20 years ago.
18:29
<kriskowal>
eemeli: try deopts the surrounding function in V8, but that’s easy to work around. best practice today is to make a tiny function with a try/catch and sandwich it between optimizable frames
18:31
<TabAtkins>
Interesting. That is basically a verbose version of the proposed expression-try. I wonder if using expression-try, then, could avoid the deopt.
18:31
<kriskowal>
that said, javascript and high performance javascript are not similar languages
18:33
<kriskowal>
While you’re thinking about this, please also think about try {} catch {} then {} so folks can continue the lexical context of the try block without being subject to the catch. It’s very common to do way too much work in the try, for the convenience of the lexical bindings there.
18:35
<eemeli>
Do you mean try {} catch {} finally {}?
18:36
<kriskowal>
I do not.
18:36
<kriskowal>
I’m proposing a block that executes only along the happy path, continuing the lexical bindings of try.
18:37
<kriskowal>
This is pretty straight-forward to express in terms of promise then chains, but much less obvious with try.
18:37
<eemeli>
That seems like a use case that try-expressions are also looking to somewhat solve.
18:42
<kriskowal>
Like, try { } catch {} is the synchronous analog of tryback().then(null, catchback) whereas try {} catch {} then {} is analogous to tryback().then(thenback, catchback)
18:43
<bakkot>
I am reasonably sure this hasn't been true for a long time now
18:44
<bakkot>
yeah since 2016 https://groups.google.com/g/v8-users/c/maH60gh_a8s
18:44
<kriskowal>
I’m prepared to believe. I last checked five years ago.
18:45
<kriskowal>
Evidently on an already old Node.js timeline. Thanks, bakkot
18:50
<ljharb>
then how can i determine if what was thrown was an error with a cause, or the falsy value? there are empirically use cases for throwing non-object values, falsy or not, and we can't be pretending that's not the case in language design.
18:56
<nicolo-ribaudo>

then how can i determine if what was thrown was an error with a cause, or the falsy value?

there are empirically use cases for throwing non-object values, falsy or not, and we can't be pretending that's not the case in language design.

It could always wrap, so you can also check .cause to see what was actually thrown
18:56
<ljharb>
lol so you'd get an error with a cause that's an error with a cause, if the latter was thrown?
18:56
<ljharb>
what's the value of that
18:57
<eemeli>
Why would you need to differentiate errors at this sort of resolution?
18:57
<ljharb>
imo things that deal with exceptions should all be consistent; you can throw any value, so you must always be able to catch any value
18:58
<eemeli>
That would still continue to be just as possible as it is today, though.
18:58
<ljharb>
exceptions can (and often are) be used as a form of flow control, like promises but synchronous. throwing undefined, null, false, etc is all something i've seen on nonzero occasions
18:58
<nicolo-ribaudo>
what's the value of that
That it prevents you from accidentally forgetting that thrown values can be falsish, which is very easy to do if you have separate .error and .ok properties
18:59
<ljharb>
as opposed to accidentally forgetting any other distinguishing things besides falsiness? what if you forget they can be truthy primitives
18:59
<ljharb>
they can also be an object with getters that throw when you access them, which you could also forget
19:00
<eemeli>
Hence my suggestion above, "always wrap a non-Error value in an Error with a cause".
19:00
<ljharb>
right. but imo that would be a very confusing inconsistency with the rest of the language, for very little benefit
19:00
<ljharb>
all exceptions are of type unknown and it doesn't make sense to me to implicitly wrap them in any context (since they can't be made to be wrapped in every context)
19:01
<eemeli>
Then the immediate result.error would always be an Error, and if you really care, you could get the original thrown value in cause, modulo the concern about the thrown error itself having a cause.
19:02
<ljharb>
"always be an error" isn't a universal or objectively correct thing to want (at least not in JS as it is)
19:02
<eemeli>
Why not? I mean, we're calling the property .error after all.
19:02
<nicolo-ribaudo>
What if we did the ADT/enum proposal first
19:02
<ljharb>
i mean we could call it .exception but the name should be descriptive rather than dictating the semantics
19:03
<ljharb>
colloquially the error is whatever a catch block would catch
19:03
<ljharb>
how does that change this?
19:03
<nicolo-ribaudo>
Then we'd have Success(value) and Failure(thrown value)
19:04
<nicolo-ribaudo>
With no need for rewrapping
19:04
<ljharb>
maybe i'm missing context on which enum proposal you mean, but enums aren't wrappers for values, they're a list of constants?
19:04
<ljharb>
or do you mean like extractors + pattern matching?
19:04
<nicolo-ribaudo>
maybe i'm missing context on which enum proposal you mean, but enums aren't wrappers for values, they're a list of constants?
I vaguely remember some version of it that allowed attaching a payload to the enum values
19:05
<ljharb>
i can't conceive of how that would work or make sense, but lmk if you find the link
19:05
<nicolo-ribaudo>
Oh it was this https://github.com/Jack-Works/proposal-enum?tab=readme-ov-file#future-steps-adt-enum
19:06
<ljharb>
i don't recall if that was presented, but since it requires pattern matching i assume the way you'd do that at this point is as an extractor
19:06
<ljharb>
but that still requires a wrapper object to be extracted from, and users wouldn't be forced to use the extractors, so you'd still need three properties, and we'd still need to bikeshed their names
19:10
<bakkot>
man I really do not like the [error, result] = whatever style proposals
19:10
<bakkot>
I do not currently think any new syntax is a good use of our time but this one seems particularly bad
19:11
<bakkot>
if someone is motivated to do anything in this area, do expressions seems like the obvious thing; then you don't need a box: let result = do { try { whatever } catch { null } } and it's on you to ensure that null is not a valid value in the happy path, or use some other value if it is
19:12
<bakkot>
I am not currently planning on doing any work on do expressions and would discourage anyone from doing any work on any syntax proposals
19:12
<bakkot>
but if someone really really wants to do so I'm not going to stop people from picking up do expressions
19:13
<bakkot>
(any syntax sugar proposals, I should say; for example module declarations are new syntax but the point is they provide a new capability)
19:39
<rbuckton>
I vaguely remember some version of it that allowed attaching a payload to the enum values
That is something I've also been considering for my version of an enum proposal
21:25
<TabAtkins>
I use this sort of syntax for other things in my Python code right now, it really doesn't feel that bad. I suppose Python has the slight advantage of not needing the [] on the LHS for tuple unpacking.
21:26
<TabAtkins>
(my parser projects are littered with val, i, err = parseFoo(...) calls)
21:44
<shu>
so lol for (using await of []) {} is fine
21:44
<shu>
also why did we hate on let let specifically but using using is fine?
22:35
<Michael Ficarra>
also why did we hate on let let specifically but using using is fine?
I think there's still time to add new contextual keyword restrictions
23:06
<shu>
for await (await using what is we doing 💀
23:07
<shu>
what about the other side? do you think using let should be allowed
23:08
<shu>
actually we already disallow const let so it should stay disallowed