SE-0276: Multi-Pattern Catch Clauses

What is your evaluation of the proposal?

+1.

Is the problem being addressed significant enough to warrant a change to Swift?

It's not critical and won't change my life, but it's a nice to have and will no doubt be one of those niceties that I would occasionally use.

Does this proposal fit well with the feel and direction of Swift?

Yes, it fits and almost feels a bit silly that it wasn't there already.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

No.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Read the proposal.

2 Likes

+1, this looks like an obvious addition to me.

This is some low hanging fruit for me and will be more than welcome. Thanks!

I have two cases where is not clear for me how this change fits the existing idioms:

Ex 1

do {
  try performTask()
} catch TaskError.someRecoverableError {
  recover()
} catch TaskError.someFailure,    // Match the exception but didn't bind msg
        TaskError.anotherFailure(let msg) {
  showMessage(msg)
}

What happen here when the catch match with TaskError.anotherFailure? Is this a compilation error?

Ex 2

do {
  try performTask()
} catch TaskError.someRecoverableError {
  recover()
} catch TaskError.someFailure(let msg) as excObject,    // Bind excObject to the exception 
        TaskError.anotherFailure(let msg) {
  print(excObject)
  showMessage(msg)
}

Similar to the previous case. What's the behavior here if the catch match with TaskError.anotherFailure?

The relevant part of the proposal is:

Similar to switch cases, catch clauses with multiple patterns may still contain value bindings. However, those bindings must have the same name and type in each pattern.

As a result, your first example results in a compiler error: error: 'msg' must be bound in every pattern.

Your second example is also invalid code because as is used to write a type-casting pattern, and the right-hand-side, excObject, is not a type.

1 Like

Just out of curiosity, why wouldn‘t we able to introduce a new syntax form such as you mentioned and slowly fade or not further extend the older one?

If we‘d introduce a switch like catch block then people will start using it and slowly over time the old version will likely go away as all new switch like features would be supported in that new form.

And one more thing, wouldn‘t your catch (switch like) block require a default case at the end?

3 Likes

For example wouldn‘t a catch block also benefit from this ability as well (assuming it gets accepted and implemented)?

do {
  ...
} catch {
case where <condition> in <pattern_1>, <pattern_2>:
  ...
}

To wrap up, I would support this proposal as it fits well as an extension of the current functionality. However after reading @beccadax‘s example, I‘m intrigued by this more general syntax form which already would benefit from all switch functionalities that we have today or in the future. So why trying to align something as closely as possible to switches rather than reusing the whole switch case mechanism for do catch?

If there is a chance to go the other way now, I‘d rather choose it and vote my -1 on the current proposed solution.


Under the assumption that we eventually get typed throws, a do catch case would automatically allow us to drop the type prefix from all the patterns. I‘m not sure if this would be the case for the current do catch though, this also depends on how many different try's are in the do block.

2 Likes

What is your evaluation of the proposal?
+1

Is the problem being addressed significant enough to warrant a change to Swift?
Yes

Does this proposal fit well with the feel and direction of Swift?
Yes

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
N/A

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
Read the proposal.

Getting off topic, but the problem with changing catch to use case is that, when I imagine the proposal, it includes this text:

Motivation

It implies that catch supports all case patterns. Also, we like it better.

It's a little late to make those kinds of changes to the language when there are much less disruptive alternatives. Don't go -1 on a hypothetical.

(Besides, the pre-open-source Swift team were a pretty clever bunch; they may have thought about this design and rejected it.)

3 Likes

+1

+1

I find Switch's pattern matching syntax to be more intuitive and easier to work with so am all for catch to be used in a similar manor.

One question, with the example...

catch TaskError.someRecoverableError {    // OK
  recover()
} 

Is there still an implicit error parameter? Is there any way to access the original error at all and/or rename it?

Basically...

catch TaskError.someRecoverableError { 
    print(error.localizedDescription) // Is this possible?
    // Also what Type would error be? `Error` or `TaskError`?
} 

No, this proposal doesn’t change the implicit error binding behavior, which remains only supported in empty catch clauses. This is addressed briefly under alternatives considered. Basically, there are still some open design questions and minor source compatibility issues which need to be resolved before extending the implicit error binding. For example, it might be useful to first introduce the notion of conjunctive patterns like let error as Error @ TaskError.someError, to borrow some syntax from Rust. The error binding could then just be syntactic sugar so users wouldn’t need to write the first part in the common case. Any changes in this area deserve their own dedicated proposal though.

Bummer. Having no way to access the original error is pretty limiting and usually means I don't use a pattern. This is an existing problem so it may indeed would be a separate proposal, but it does mean it's and incomplete replacement of the pattern pointed to as the motivation since with the catch let error as TaskError; switch error { pattern the cases still have access to error.

compared to...

do {
    try performTask()
} catch TaskError.someFailure(let msg),
        TaskError.anotherFailure(let msg) {
    showMessage(msg)
    // What if it should re-throw?
    // What if logging more info other than `msg` is required?
    // Also, no additional information available during debugging 😬
}

Not really a detraction from the proposal, so still +1 from me, but kind of a bummer I won't be able to justify using it :disappointed:

1 Like

Sidenote...didn't realize emoji get italicized :smile:

4 Likes

What is your evaluation of the proposal?
+1

Is the problem being addressed significant enough to warrant a change to Swift?
Yes

Does this proposal fit well with the feel and direction of Swift?
Yes

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
I’ve read the proposal and followed the discussion. And I’ve looked at code bases where the error is switched on in catch clauses - just like the proposal mentions. This will be nice for consistency and conciseness.

I am afraid of addition that makes language syntax more divergent for sake of some potential unclear improvement. With such rapid speed of reinventing wheels, soon there will be only remnants of what swift used to be: clear, (mostly) unified syntax.

-1 :worried: @Chris_Lattner3

1 Like

Wouldn’t a change like that be forward compatible with the current implicit error? That seems like any new behavior could be potentially be additive, and the benefit of still having the error but maybe having to cast it inside of the block wouldn’t be overly burdensome. Then at a later time if it implicitly bound to a more specific error type it shouldn’t break any existing code, right?

This reply isn’t terribly constructive. Can you explain why you think having multiple error patterns in a catch clause makes the language less clear than the current situation where you have to nest a switch statement inside the catch? You say that the syntax is “(mostly) unified”, but doesn’t making catch patterns act like other case clauses make the language more unified, not less?

3 Likes

Potentially, depending on the final design. It’s a little too early to say what the exact semantics would be though. I don’t think a follow up proposal to expand implicit error binding would be all that controversial, but it’s mostly orthogonal to this proposal.

  • What is your evaluation of the proposal?

+1, it seems like an obvious extension of the error handling syntax.

  • Is the problem being addressed significant enough to warrant a change to Swift?

Yes, at least as far as it's inline with pattern matching seen elsewhere and enhances an important feature of the language.

  • Does this proposal fit well with the feel and direction of Swift?

Generally yes, though the intersection of previous catch behavior and pattern matching like this, namely around the implicit error value, leaves a sharp edge. However, it looks like this can be dealt with fairly easily.

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

No.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Quick reading of the proposal.