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. |
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 |
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 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 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> |
|
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 |
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? |
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 |
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 |
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 |