Skip to content
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

Closed
rafageist opened this issue Aug 23, 2024 · 29 comments
Closed

Comments

@rafageist
Copy link

rafageist commented Aug 23, 2024

I’ve been following the discussion around the introduction of the ?= operator and the alternative try 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:

  • Balance Between Brevity and Clarity: The desire for a one-liner often conflicts with the need for explicit and clear error handling, which is crucial in JavaScript's dynamic environment.
  • Consistency with Language Philosophy: JavaScript has traditionally favored explicit and readable code over syntactic shortcuts that could obscure meaning. This is a principle that has kept the language accessible and understandable to a broad range of developers.
  • Community Feedback: The ongoing discussion also demonstrates that any changes to error handling need to be carefully considered, with input from the community and a thorough evaluation of the potential trade-offs.

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 of try, 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 the try 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:

  • Void Functions: Functions that perform actions like modifying a global state, writing to a file, or sending a network request may throw exceptions without returning any value. An operator like ?= or a one-liner try would be ill-suited for these cases, as there would be no value to assign or capture.
  • Asynchronous Side Effects: In asynchronous operations, exceptions can occur at various points that are not directly tied to value-returning expressions. For example, a background task might fail due to network issues or file system errors, and these exceptions need to be handled explicitly, often without a direct return value.
  • Event Handling and Callbacks: In event-driven programming, exceptions can occur within event handlers or callbacks that are meant to trigger certain actions rather than return values. In these cases, structured error handling via 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 compact try 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 the try 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. While try-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:

async function getData() {
    try {
        const response = await fetch("https://api.example.com/data");
        const json = await response.json();
        return validationSchema.parse(json);
    } catch (error) {
        handleError(error);
    }
}

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 alternative try 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:

  • Compatibility and Complexity: Introducing a new operator or keyword would increase the language's complexity, requiring developers to learn and adapt to new syntax, potentially leading to confusion or misuse.
  • Existing Mechanisms: JavaScript's current error handling mechanisms, while sometimes verbose, are explicit and clear. They ensure that developers consciously manage errors, which is crucial for maintaining robust code.
  • Ambiguity and Readability: The proposal risks making error handling less explicit by integrating it into an assignment operator or simplifying it into a one-liner with 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 Justified

Using 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:

  • Consistency with try Semantics: Using try 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 by catch to handle them. Introducing try as part of an assignment expression like [error, result] = try doSome(); might confuse developers who are already familiar with traditional try-catch usage.
  • Explicit vs. Implicit: Using try in an assignment might make exception handling less explicit. One of the strengths of try-catch is that it makes it clear that a potential exception is being handled, whereas introducing try in an assignment might make error handling more inconspicuous, potentially leading to errors of omission.
  • Exception Handling and Control Flow: try-catch allows for fine-grained handling of control flow, including the possibility of multiple catch and finally blocks (of course this is not possible in JS now). Using try in an assignment limits these capabilities and might require additional handling if different types of errors need to be caught and handled.
  • Compatibility and Learning Curve: While it might seem simpler at first glance, this new form of 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 modified try 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.

@arthurfiorette
Copy link
Owner

try-catch allows for fine-grained handling of control flow, including the possibility of multiple catch and finally blocks.

no, it DOES NOT.

Are we being chat-gpt'ed here?

@DScheglov
Copy link

DScheglov commented Aug 23, 2024

@arthurfiorette

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 nice-try lib works both: it is easy to use the library, but it also, or may be even more effectively proves that comunity needs a way to assign an error directly in the function scope.

@rafageist

The point that ?= invents something new in error handling in JS is absolutely incorrect. NodeJs started with exactly this approach for callbacks, and years passed before we get Promise and async/await. So, EcmaScript moves to flat code, and the ?= is a perfect facility for such movement ...

@arthurfiorette
Copy link
Owner

I never thought I would be chat-gpt'ed this hard in my life

@rafageist
Copy link
Author

@arthurfiorette @DScheglov

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 async/await improves readability, adding a new operator like ?= might introduce confusion instead of clarity.

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.

@arthurfiorette

This comment was marked as off-topic.

@arthurfiorette arthurfiorette self-assigned this Aug 23, 2024
@arthurfiorette arthurfiorette added the documentation Improvements or additions to documentation label Aug 23, 2024
@rafageist
Copy link
Author

@arthurfiorette

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.

some = "foo"

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 try more:

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

@DScheglov
Copy link

@arthurfiorette

Multiple catch blocks in other languages serve specific purposes that may not translate well into JavaScript's asynchronous and event-driven nature.

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

Actually ?= works well with multiple catch emulation:

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;
}

@rafageist
Copy link
Author

rafageist commented Aug 23, 2024

@arthurfiorette

Multiple catch blocks in other languages serve specific purposes that may not translate well into JavaScript's asynchronous and event-driven nature.

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

Actually ?= works well with multiple catch emulation:

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

@arthurfiorette
Copy link
Owner

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

@arthurfiorette arthurfiorette removed the documentation Improvements or additions to documentation label Aug 23, 2024
@arthurfiorette arthurfiorette removed their assignment Aug 23, 2024
@DScheglov
Copy link

DScheglov commented Aug 23, 2024

@rafageist

This is an old topic!!

https://stackoverflow.com/questions/19080382/appropriate-inline-try-catch

-- it is old, but it is not about the error handling: it is about using semicolons

https://github.com/craigmichaelmartin/inline-try

-- it is not old, A year ago.

https://www.reddit.com/r/programming/comments/15kjzx9/a_javascript_inline_try_for_readability_and/

-- it is a discussion of the inline-try library and it is also a one year.

For example the nice-try library has been first time published 8 years ago.

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.

We are preventing propagation of exception using try ... catch, and in catch(err) we convert the exception to an unsuccesful result of the function.

The proposal offers the same, but just in less verbose way.

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.

The first of all, it is exactly an opinion. Currently this opinion is expressed by you.
Objective fact could be evidanced or mesured, but you just claims, no proofs, no arguments.
So, currently it is just a ground-less opinion.

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.

To have an opportunity doesn't force you to apply this opportunity.
How often to use the ?= must be kept out of the proposal. All of us can have our own error handling strategy, so "catch 1 or 2 times" -- is just your one, please don't push others to follow.

@DScheglov
Copy link

@arthurfiorette

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.

For non-native English speakers, such phrases may seem like unimportant local idioms. So, it could be ignored in this reason.
As instance I've ignored it as well. So, please, be careful with such checks )) it could be a false positive

@DScheglov
Copy link

@anacierdem

have you read the #33 (comment) before liking???

@anacierdem
Copy link

Yes, why?

@arthurfiorette
Copy link
Owner

RobotLokiGIF

@rafageist
Copy link
Author

@arthurfiorette Remember, this isn't Twitter or Facebook. TC39 will read all of this and take it into account when analyzing your proposal.

@DScheglov
Copy link

DScheglov commented Aug 23, 2024

@anacierdem

Adding more layers of abstraction (such as using ?= to emulate multiple catches) can reduce the clarity of your code

Doesn't confuse you, no? )
it is really written by ChatGPT or something like that ...

@rafageist
Copy link
Author

rafageist commented Aug 23, 2024

@DScheglov

The first of all, it is exactly an opinion. Currently this opinion is expressed by you. Objective fact could be evidanced or mesured, but you just claims, no proofs, no arguments. So, currently it is just a ground-less opinion.

When I say that it is not my opinion and it is a fact, it is for the following reasons:

  • The result of the functions go one way
  • The exceptions go another way

You are using an assignment operator, historically known to obtain the result of the right in the left. And in the same way you are joining the result with the exception. And of course you will have to make a branch to be able to know if there was an error or not or ignore it completely.

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.

@anacierdem
Copy link

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

@arthurfiorette
Copy link
Owner

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.

@arthurfiorette arthurfiorette closed this as not planned Won't fix, can't repro, duplicate, stale Aug 23, 2024
@DScheglov
Copy link

@rafageist

When I say that it is not my opinion and it is a fact, it is for the following reasons:

  • The result of the functions go one way
  • The exceptions go another way

Well, with promise rejection, we have an assignment and different flows for a
fulfilled value and a rejected reason. It's just that the assignment and control
flow splitting are not mutually exclusive options. The same we have with ?.
operator and with custom types like Either (or Result).

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 result and error, or push them to the array, or do something else and then delegate the flow
spiting to the calling side, or vise versa we can call something to process the [error, result]
tuple.

The difference between error + result assignment and try .. catch:

  • with try ... catch we split flow and then perfom assignmenr
  • with error + result assignment we perform assignment and then split the flow.

In scope of single function when the try .. catch or error + result assignment is used,
the second one leads to more flat code, then try .. catch. The rest keeps the same.

Another (more important) difference is the relatively explicit error ignoring with try ... catch
and the relatively implicit ignoring with assignment:

  • With try .. catch the error ignoring looks like an empty catch block
  • With assignment we have unused error variable.
  • For the ?= we also can omit the error assignment at all:
    const [, response] ?= await fetch("https://api.example.com");

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 try ... catch there is eslint rule: no-empty
For ?= the new eslint rule will be needed.

So, @rafageist , if your point is that any error + result assignment tends to implicit error ignoring,
more then try .. catch does, then I agree with you. Yes, such kind of assignment tends to implicit
error ignoring, because developer is not forced to handle the error, especially in JS or relaxed TS.

@arthurfiorette ,
the same claim regarding the ?= was pointed in other issue: #9 (comment)

So, perhaps it makes sense to create a separate issue and close all other ones that results with this "implicit error ignoring"

@arthurfiorette
Copy link
Owner

arthurfiorette commented Aug 23, 2024

@anacierdem @DScheglov @rafageist

Just my final words here:

Yes, such kind of assignment tends to implicit
error ignoring, because developer is not forced to handle the error, especially in JS or relaxed TS.

The error can be suppressed easier? I don't think so:

const [, res] ?= await fetch("https://api.example.com");

try {
  const res = await fetch("https://api.example.com");
} catch {}

Same syntax, same results.

However with the ?= the error can only be suppressed if the function shouldnt produce any meaningful value, since the res (in the above example) will always be undefined in case of errors, probably throwing a "cannot access property X of undefined" in the next statement that uses it.

This tweet from Lea Verou express some of the feelings that the proposal wants to achieve, the try {} block is useless.

The catch (error) syntax, just as ?=, also captures an exception (different flow) and transforms it into a value. On of the problems is that with catch (error) you must have a try and bring all of his badness with it.

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 {}

@DScheglov
Copy link

@arthurfiorette

This tweet from Lea Verou express some of the feelings that the proposal wants to achieve, the try {} block is useless.

You are taking the one part of Lea's post and ignores another one:

  • try {} is useless
  • catch {} is important

And looking on your example it seems it is much more easy
to lost the catch {} with ?= then with try .. catch

The error can be suppressed easier? I don't think so:

const [, res] ?= await fetch("https://api.example.com");

try {
  const res = await fetch("https://api.example.com");
} catch {}

Same syntax, same results.

Almost the same. The empty catch block is explicit, the omit error in the tuple
destructuring is definitely implicit.

The problem is not in the ignoring. The problem is that error is implicitly ignored.

@arthurfiorette
Copy link
Owner

catch {} is important

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

@DScheglov
Copy link

@arthurfiorette

Exactly, purpose of catch is important.
But with ?= we have additional obstacles to achieve that purpose:

  1. Explicit need to write if. With try .. catch we cannot omit the catch
  2. In general case if (error) is not equal to if (error != null) and both of this is not equal to try ... catch(err). When we work with only own code -- we can assume that all errors are instances of Error, if we are using puclic package registry, we are using code written by hundred or even thousands of developers, that can be changed in some moment without to be reviewed. It is important.

So, trying to reach the more safe code with ?= we can get much more less safe code.
And this fact has its own price:

  • we need an additional linting rules
  • we need an additional effort on code review

So, despit I really like ?= or try I'm admitting these cons of the approach.
And I'm thinking this cons could be compensated.

By the way, we also have a do expression proposal,
and when it will be implemented we will get opportunity to write:

const data = do { try { 
  await fetch("https://api.example.com")
} catch (err) {
  handleFetchError(err);
  return;
}};

So, in context of "try {} block is useless", it makes sense to extend the do-proposal with adding catch block directly to do
and get the following:

const data = do { 
  await fetch("https://api.example.com");
} catch (err) {
  handleFetchError(err);
  return;
};

Doesn't it?

@khaosdoctor
Copy link

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 JSON.parse which throws, so if you want to check if something is a JSON you must do this:

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 class syntax or append methods directly to a function prototype or static-link them. And I think that's the actual beauty of this language, it embraces all styles.

@khaosdoctor
Copy link

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

@rafageist
Copy link
Author

rafageist commented Aug 27, 2024

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

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.

@khaosdoctor
Copy link

khaosdoctor commented Aug 27, 2024 via email

@rafageist
Copy link
Author

@khaosdoctor This is closed and I believe everything has been addressed and it is up to the developers and TC39 to resolve it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants