-
-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reconsidering the Need for a New Operator in Error Handling Proposals #33
Comments
no, it DOES NOT. Are we being chat-gpt'ed here? |
It really seems like a response by ChatGPT ... By the way, my argument regarding 12mln weekly downloads of The point that |
I never thought I would be chat-gpt'ed this hard in my life |
Thank you for the feedback. I'd like to clarify a few things. On try-catch in JavaScript: I understand there might have been confusion regarding my mention of multiple catch blocks. While JavaScript doesn’t currently support multiple catch blocks, my comment was meant to discuss exception handling and control flow from a broader, more theoretical perspective. In many languages that use similar structures, having multiple catch blocks allows for more fine-grained error handling. This concept has also been discussed within the Typescript community as a potential future enhancement, which could provide even greater flexibility in error management. Regarding Libraries Like nice-try: Popular libraries solve specific problems, but their existence doesn’t always indicate a need for the language itself to change. Not every tool or shortcut needs to be built into the language core, especially if it could add unnecessary complexity. Flat Code and the ?= Operator: While writing "flat" code with As for my thoughts, they come from decades of experience with various languages and computing generations. I value open discussions aimed at improving JavaScript for everyone. While new ideas are always welcome, it’s also important to respect the experience others bring to the conversation. I’ve carefully considered these proposals and aimed to provide constructive feedback. The goal isn’t to convince me personally but to present solid, well-reasoned arguments that will stand up to scrutiny by the TC39 committee. I hope we can continue this dialogue with mutual respect for the time and expertise everyone contributes. |
This comment was marked as off-topic.
This comment was marked as off-topic.
This is an old topic!!
The problem lies in the misconception between exception and return value of a function. A function can return values and part of those values can be error codes. But an exception breaks the flow of that function, or even of the entire program. Historically this syntax is known as assignment, and in any of its variants even conditional in modern languages. What is on the right is assigned to the memory area on the left.
In PHP for example we have this $bar ??= "foo"; Which is simply a shortcut for $bar = $bar ?? "foo"; And so on in many other languages, but all with the same syntactical concept of assigning. For this reason many people liked using [error, result] = try await fetch(... However, I consider it unnecessary, and more a contribution to laziness than to the improvement of language, because conceptually it is wrong and that is not my opinion, but an objective fact in the creation of languages, which are not arbitrarily altered. There are ways to deal with these problems such as having a solid architecture where you may write try catch 1 or 2 times, or not at all. Error handling from decades is not the result of a "magic assignment" or some kind of special operator that automatically handles exceptions. Instead, it relies on explicit instructions and clear flow control. |
Common. A single catch is due to a simple reason: ES has no syntax to express the type of a value. Therefore, it would be illogical to invent a special typing syntax for caught exceptions. Perhaps when we get the types in ES or at least a pattern matching -- we will get and multiple Actually type ErrValueTuple<T> = readonly [{}, undefined] | readonly [undefined, T];
function fn([error, value]: ErrValueTuple<number>) {
if (error != null) switch (true) {
case error instanceof TypeError:
// do something
return; // or throw
case error instanceof ReferenceError:
// do something
return; // or throw
default:
throw error;
}
return value * 2;
} |
Adding more layers of abstraction (such as using ?= to emulate multiple catches) can reduce the clarity of your code. This could make error handling less intuitive and harder to follow, especially for developers who are not familiar with this technique. Although other programming languages have type systems and allow for more robust error handling, simply importing those ideas into JavaScript may not be the best solution due to the nature of the language. JavaScript already allows for explicit error handling with try-catch, and techniques such as using functions to handle errors in one place can be employed, which can keep your code cleaner and more organized. If the future of ECMAScript includes a more robust type system or pattern matching features, this may open up new possibilities for handling multiple types of errors in a way that integrates better with the current language, without the need for additional operators like ?=. |
@DScheglov sorry for making you reply to the above, i just give up on his chat gpt replies and simply asked chatgpt to reply him as well 😅 Tried to confirm with: Also help me on how to say good morning in spanish, as its one of my biggest questions but he didn't said a thing about it so I'm almost sure its not a real human there. |
-- it is old, but it is not about the error handling: it is about using semicolons -- it is not old, A year ago.
-- it is a discussion of the For example the nice-try library has been first time published 8 years ago.
We are preventing propagation of exception using The proposal offers the same, but just in less verbose way.
The first of all, it is exactly an opinion. Currently this opinion is expressed by you.
To have an opportunity doesn't force you to apply this opportunity. |
For non-native English speakers, such phrases may seem like |
have you read the #33 (comment) before liking??? |
Yes, why? |
@arthurfiorette Remember, this isn't Twitter or Facebook. TC39 will read all of this and take it into account when analyzing your proposal. |
Doesn't confuse you, no? ) |
When I say that it is not my opinion and it is a fact, it is for the following reasons:
You are using an assignment operator, historically known to obtain the result of the So that you understand the difference, why not these other proposals? I let my imagination fly: let result = try fetch("https://api.example.com") or catch err;
let result = fetch("https://api.example.com") catch err;
try fetch("https://api.example.com") into result or catch err;
try result = fetch("https://api.example.com") catch err; // I liked it!
try {
let result = fetch("https://api.example.com");
} catch (n when n instanceof NetworkError) {
}
catch (e when e instanceof Error) {
}
// ...
try {
let result = fetch("https://api.example.com");
} catch (n instanceof NetworkError) {
}
catch (e instanceof Error) {
}
// ... Shall I continue? We are then talking about assignment vs flow control. And this is an objective problem. |
@DScheglov No that didn't confuse me. Even if this is the pure output of an LLM, I think it is very much capable of understanding intricacies of this topic, which is indeed funnier. I think it's about time unwatching this repo. |
Ok, I'm closing this PR because I don't see a good vibe between everyone. Feel free to create more issues and join other discussions. I kindly ask that when creating a post to use all dedication possible to write in an authentically way or reference articles that do so. This way we can have an insightful conversation that brings good results to the proposal idea. |
Well, with promise rejection, we have an assignment and different flows for a In the following cases: let [error, response] ?= await fetch("https://api.example.com");
// including yours -- I guess some of this options really could lay on the table
let result = try await fetch("https://api.example.com") or catch err;
let result = await fetch("https://api.example.com") catch err;
try await fetch("https://api.example.com") into result or catch err;
try result = await fetch("https://api.example.com") catch err; // You liked it! We have an assignment, and then we can split flow if we need. We also can return The difference between
In scope of single function when the Another (more important) difference is the relatively explicit error ignoring with
In TypeScript we have a better situation (but only if TS is correctly configured for strict null checks). In all cases, detecting implicit error ignoring requires applying a corresponding linting rule. For the So, @rafageist , if your point is that any @arthurfiorette , So, perhaps it makes sense to create a separate issue and close all other ones that results with this "implicit error ignoring" |
@anacierdem @DScheglov @rafageist Just my final words here:
The error can be suppressed easier? I don't think so:
Same syntax, same results. However with the This tweet from Lea Verou express some of the feelings that the proposal wants to achieve, the The About ignoring/supressing/forgetting/etc, it will happen in the same frequency. // You can't do the following: because res will be T | undefined
// forcing you to do a if (res) in the next statement, which effectively is the same
// thing as handling the possibility of error.
const [, res] ?= await fetch("https://api.example.com");
// The only "valid" solution for that is when the method returns void,
// i'm not even sure if this is a valid syntax
const [] ?= await submit();
// which is visually the same as doing:
try {
await submit();
} catch {} |
You are taking the one part of Lea's post and ignores another one:
And looking on your example it seems it is much more easy
Almost the same. The empty catch block is explicit, the omit error in the tuple The problem is not in the ignoring. The problem is that error is implicitly ignored. |
The purpose of catch is important const [error, res] ?= await fetch("https://api.example.com");
if (error) {
// has the same effect as catch block, but without scoping res.
} |
Exactly, purpose of catch is important.
So, trying to reach the more safe code with
So, despit I really like By the way, we also have a do expression proposal, const data = do { try {
await fetch("https://api.example.com")
} catch (err) {
handleFetchError(err);
return;
}}; So, in context of " const data = do {
await fetch("https://api.example.com");
} catch (err) {
handleFetchError(err);
return;
}; Doesn't it? |
I do agree that errors are easier to be ignored in this proposal, but it also solves a fundamental problem in all try/catch blocks: the scope. My main pain point for over 10 years has been let result
try {
result = JSON.parse(something)
} catch (err) { ... } This is even worse when you're doing schema checking (for example, with Zod), because you need to nest the try block inside the zod validation and check both for the ZodError and the JSON error, and a simple "is this JSON" becomes a mess. And also, I do agree that try is negligible but catch is the catch (pun intended), so we need to look for a way to streamline error handling, currently I think Go is the best error handling we can have, because the compiler forces you to check for errors. This isn't possible in JS, but ESLint is there exactly to do this. While I agree that there are downsides in some things, there's also the fact that people can continue to use try/catch, the same way people can use |
And a side note here, I'm not sure what was the initial discussion because we've been tapping the same key since #24 and mentioning multiple times the same multiple catch block proposal (https://github.com/rafageist/proposal-multiple-catch-when that I liked too) which would be rendered useless if this passes since we could then pattern-match/switch/if-else our hearts to the error classes. This is not the point, the point is about making error handling easier and to have at least one option to handle errors, whereas today we have none besides |
Issue #24 is about the fact that the current language can solve this problem without introducing an operator. This issue is about reconsidering the proposal because the arguments presented are not sufficient and the TC39 committee will be rigorous about it: an assignment operator should not have the responsibility of flow control. The most that has been achieved is conditional assignment. An exception is a branch in the program flow. |
But it doesn’t! That’s the point. It’s an assignment operator. When you
assign the error to the variable, you can control the flow the way you want
it to be controlled.
Flow control structures like if and else will be used, it’s not the
assignment operator that’s controlling the flow. We could also argue that
the proposal of promise with resolvers is also a flow-controlling
assignment because you’re decoupling essentially the flow control from the
promise callback.
Even more so, callbacks are controlled exactly like this. You receive two
parameters, errors and data, if the error is not null you can handle, but
you won’t use a try catch inside a callback to handle its errors. So this
is already present in the language, we’re just exposing it to other
functions that don’t use a callback.
|
@khaosdoctor This is closed and I believe everything has been addressed and it is up to the developers and TC39 to resolve it. |
I’ve been following the discussion around the introduction of the
?=
operator and the alternativetry
keyword proposal for error handling in JavaScript. While I still consider it unnecessary (#24) and appreciate the effort to improve error handling, I think both approaches may introduce more complexity than necessary, and I'd like to share my thoughts on why we should reconsider these changes.The survey #4 results align with what is commonly accepted in the theory and practice of building interpreters and compilers: error handling is better managed through explicit keywords like
try
, rather than overloading an assignment operator with additional responsibilities. This approach keeps the language clean, maintainable, and intuitive, ensuring that error handling remains clear and effective.Historical Context and Ongoing Debate
The conversation around simplifying error handling with a one-liner like
try-catch
in JavaScript has been ongoing since at least 2019 (#15), as seen in this Discourse thread. This history highlights that while there is a desire within the community for more concise error handling, it's also a topic that has generated significant debate.The fact that similar ideas have been proposed and discussed for several years without being adopted into the language suggests that there are valid concerns and complexities involved. Key issues include:
Given this context, it's important to approach any new proposals with a full understanding of the history and the reasons why previous attempts at similar changes were not adopted. This should inform whether new syntax or operators are truly necessary or if they simply reintroduce the same unresolved issues.
Theoretical and Practical Considerations
Firstly, it’s important to consider the principles of compiler and interpreter design. In "Compilers: Principles, Techniques, and Tools" by Aho, Sethi, and Ullman, a clear separation of concerns is emphasized. The idea is that language constructs should be as clear and focused as possible, with specific constructs like operators and keywords serving distinct purposes. Introducing a new operator like
?=
for error handling, or even modifying the use oftry
, blurs the lines between error handling and value assignment, potentially complicating both the language and its interpretation.Not All Errors Occur in Value-Returning Operations
One of the critical assumptions in the proposal for the
?=
operator and thetry
keyword modification is that exceptions primarily occur in operations that return values. However, this is not always the case. In many scenarios, exceptions can arise in functions or operations that execute tasks without returning a value, such as:?=
or a one-linertry
would be ill-suited for these cases, as there would be no value to assign or capture.try-catch
is essential to ensure that exceptions are properly managed without relying on value assignment.These examples highlight that error handling in JavaScript is a broader and more complex problem than just managing exceptions in value-returning functions. A one-size-fits-all solution like
?=
or a compacttry
expression might overlook these nuances, leading to incomplete or inadequate error management strategies.While the proposal aims to streamline error handling, it overlooks the diversity of scenarios where exceptions can occur. Error handling needs to be flexible enough to accommodate both value-returning and non-value-returning operations. This reinforces the importance of explicit and comprehensive mechanisms like
try-catch
, which can handle a wide range of error situations in a clear and consistent manner.Less Code Doesn’t Always Mean a Better Language
One of the underlying motivations for the
?=
operator and thetry
keyword proposal seems to be the desire to reduce the amount of code required for error handling. While it's true that shorter code can sometimes improve readability, it’s important to remember that brevity should not come at the cost of clarity, maintainability, or correctness.In language design, the goal is not just to reduce the number of lines of code but to ensure that the code is intuitive, expressive, and easy to reason about. Explicit error handling mechanisms like
try-catch
serve these purposes well because they clearly delineate where and how exceptions are managed. Introducing shortcuts or more compact syntax, such as an assignment operator that also handles errors, could obscure the logic, making it harder for developers to understand and maintain the code.Moreover, shorter syntax doesn’t necessarily result in better performance or fewer errors. In fact, it can introduce new complexities for the language interpreter and make debugging more challenging. The focus should be on writing clear, explicit code that accurately reflects the developer's intent, rather than simply aiming to reduce the number of characters typed.
So, while the proposal aims to make error handling more concise, it's essential to consider whether this truly leads to better code. Sometimes, more code -if it's clearer and more explicit- can be the better approach.
Handling Errors with Existing Constructs
JavaScript already provides powerful tools for error handling through the
try-catch
mechanism. Whiletry-catch
can sometimes lead to nested code or less elegant syntax, the clarity and explicit nature of this approach ensure that developers are fully aware of when and how errors are being handled. This explicitness is crucial, as it reduces the likelihood of accidentally ignoring or mishandling exceptions.For example, consider the following traditional error handling approach:
This structure is clear in its intent and allows for robust handling of different error types. Introducing
[error, data] = try doSome();
or[error, data] ?= expression;
might reduce the lines of code, but it could also obscure the error handling logic, making it less obvious to those reading or maintaining the code.Runtime vs. Compile-Time Errors
It’s also essential to distinguish between different types of errors. Syntax errors, caught at compile-time by the interpreter, are different from runtime exceptions that occur during the execution of the program. JavaScript’s current error handling mechanisms are well-suited to managing runtime errors explicitly. Delegating this responsibility to an operator or integrating it into a simplified assignment could result in subtle bugs or missed error handling opportunities.
Insufficient Justification in the Proposal
Upon reviewing the README of the proposal, it becomes clear that while it identifies some real challenges with error handling in JavaScript, it does not sufficiently justify why a new operator like
?=
or even the alternativetry
syntax is necessary or superior to existing methods.The proposal mentions the potential for simplifying error handling and reducing code nesting, but it doesn't provide concrete evidence that these benefits outweigh the downsides of introducing new language features. For instance:
try
. This could lead to errors being overlooked or improperly handled, reducing the overall readability and maintainability of the code.The proposal does not convincingly argue that these changes would result in a net benefit for the language. The introduction of such features should be carefully considered, especially when existing solutions are already well-understood and effective.
The New Proposal to Use
try
on a Single Line is Also Not JustifiedUsing
try
as a keyword instead of an operator like?=
might be a more acceptable alternative, as it would be more consistent with existing practices in JavaScript. However, even this modification raises some questions and challenges:try
Semantics: Usingtry
in this way would be a significant change to current JavaScript semantics. Currently,try
is used to start a block that may contain exceptions, followed bycatch
to handle them. Introducingtry
as part of an assignment expression like[error, result] = try doSome();
might confuse developers who are already familiar with traditionaltry-catch
usage.try
in an assignment might make exception handling less explicit. One of the strengths oftry-catch
is that it makes it clear that a potential exception is being handled, whereas introducingtry
in an assignment might make error handling more inconspicuous, potentially leading to errors of omission.try-catch
allows for fine-grained handling of control flow, including the possibility of multiplecatch
andfinally
blocks (of course this is not possible in JS now). Usingtry
in an assignment limits these capabilities and might require additional handling if different types of errors need to be caught and handled.try
would add a new syntax to the language, requiring developers to learn and understand its nuances. It could also have implications for backward compatibility, and for tools that parse or process JavaScript.In short, while using
try
instead of an operator might be a less invasive approach, it is still a significant modification that introduces new semantic considerations and potential points of confusion. It is crucial to assess whether the benefits of this new syntax outweigh the potential drawbacks and whether it actually addresses a critical enough problem to warrant the change in the language.References and Considerations
Before making such significant changes to the language, it’s worth revisiting foundational texts like "Compilers: Principles, Techniques, and Tools" and "JavaScript: The Definitive Guide" by David Flanagan. These resources underscore the importance of keeping language features both powerful and clear, without overloading them with additional responsibilities that might compromise their original purpose.
Conclusion
While the intent behind the proposal is commendable, the addition of the
?=
operator or a modifiedtry
keyword for error handling may not be the best path forward. JavaScript’s existing error handling mechanisms, though sometimes verbose, offer a level of clarity and explicitness that is crucial for maintaining robust and understandable code. I would urge the committee to consider whether these proposed changes truly solve a critical problem or if they risk introducing unnecessary complexity into the language.Thank you for considering my input.
The text was updated successfully, but these errors were encountered: