4 ms·
Agree with the point on statically typed errors. However, hasn’t it been possible to define a custom Error type since Swift 1? Or did you mean the typed throws?
by rTX5CMRXIfFG 1y ago
Agree with the point on statically typed errors. However, hasn’t it been possible to define a custom Error type since Swift 1? Or did you mean the typed throws?
Have also been picking up Java and the thing that comes to my mind is sealed classes, and whether they are similarly bad design.
- vips7L 1y agoWhy would sealed classes be bad design? It helps the compiler and you ensure that you have correct code and have handled all cases.
- MBCook 1y agoIt’s typed throws specifically. Before Swift 6 a function either could throw nothing or could throw anything, like Java’s “throws Exception”. Now you can mark that a function can only throw a specific error type but nothing else, like Java’s “throws MyCustomException”. Seems you can’t list multiple types. You’d have to have them in a class hierarchy and mark the base type. What I like about this idea: if a function is marked like this, Swift can tell that a catch block is exhaustive without needing a “catch all” error case. And if the function starts throwing more possibilities than you coded for, e.g. new case added to an enum, you get a compiler error! Not a submarine bug.
- dcrazy 1y agoThat last bit is great for application authors but awful for API designers. :)
- MBCook 1y agoYeah it could be risky. Might be a feature best used inside the library implementation but not exposed in the public interfaces. I’m not sure how this would interact with the API compatibility Swift has. Maybe that would help authors.
- conradev 1y agoThe best API design is likely to erase the public type to the `any Error` existential … … but Swift has also the tooling to declare ABI-stable enums without freezing them. Clients are forced to handle unknown cases.
- jayd16 1y agoWhy do you need to restrict to a single base type to exhaustively catch all listed?
- MBCook 1y agoFrom my reading you can’t say “throws(MyError, BadError, ThirdError)”. So you only have two options. You could use a non-checked throws like before, but we’re assuming you want it checked. Since you can only put one name between those parenthesis, the only way to do all three would be to have a base class and list that. Or at least that’s my understanding. However if you do that and there’s a fourth type called UnwantedError, that’s also valid to throw even though you don’t want to. It’s an interesting limit. I didn’t follow this feature, though I remember hearing about it, so I don’t know why that choose was made.
- Someone 1y ago> the only way to do all three would be to have a base class and list that I think the Swift way would be enum FooError: Error { case myError(MyError) case badError(BadError) case thirdError(ThirdError) } and declare your function as throws(FooError). That forces handlers of those exceptions to consider all cases. Edit: I think there’s a case for having the compiler synthesize that if one writes something like throws(MyError||BadError||ThirdError), but I’m not sure I’d would like to have such magic. Would two functions with such a declaration throw the same type of exception? That might matter if you do stats on exceptions logged, for example)
- MBCook 1y agoIt does work that way, but I was thinking of the case where FooError was one of the three types you wanted to throw. You can also declare errors as structs, so that’s where you could use inheritance.
- dcrazy 1y agoThe new feature here is typed throws, which I referred to as statically typed errors. You’ve always been able to throw a subtype of Error and then dynamically downcast it at runtime, but function signatures were limited to “throws any subtype of Error” or “doesn’t throw at all.” Sealed classes fall into the realm of things that the Swift language designers would leave up to convention. For example, using underscored public methods for internal methods that need to have external linkage, rather than inventing a new keyword for this purpose.