[swift-evolution] Remove Failable Initializers

Haravikk swift-evolution at haravikk.me
Mon Mar 7 05:43:05 CST 2016


> On 6 Mar 2016, at 09:22, Jean-Daniel Dupas <mailing at xenonium.com> wrote:
> 
> Just my though, but if this is about redundant feature, why limiting the discussion to initializer. Why not remove Optional as a return type, as any method that return a nil optional on failure may as well throw.

Because methods and properties are different; for example, the .indexOf() method on a collection can be called with an element that doesn’t exist within the collection, this isn’t an error as the method doesn’t require you to only request elements that can be found, so forcing that to throw would make no sense.

However every case I’ve seen for failable initialisers seems to be modelling some type of actual error (e.g- invalid data) as opposed to input being valid but the result is nothing, so it still seem to me like it should be using error handling, but I think at this point I’ve said more than enough on the subject.

> On 6 Mar 2016, at 03:01, Brent Royal-Gordon <brent at architechies.com> wrote:
> 
> I suppose it would be possible for the compiler to duplicate the function and provide a second implementation for `try?` to use which signals pass/fail in one bit without any details. In private APIs (or internal APIs with testability disabled), it might even be able to optimize away the unused variant. But this is going to make the app bigger and its cache performance worse.

A duplicate shouldn’t be necessary; functions/initialisers that can throw just need to be called with a flag indicating whether they should capture or ignore errors as appropriate for try vs try? and try!. Any statement that is identified as being specific to a throw is then wrapped in a conditional based on this flag so it can be skipped if errors are ignored. The throw itself will then either return the error (if it was generated) otherwise it simply indicates that an error occurred and nothing more. The compiler should already be grouping throw-specific statements together where possible, so that they aren’t visited during a non-error path so this shouldn’t add many of these conditional checks at all (in most cases there should only be one, and it will only be tested in the throw branch).

> All languages have redundant features. `+` is redundant—we have `appendContentsOf` and `addWithOverflow` (this one even provides additional error information!) and all sorts of other things which make `+` unnecessary. And yet we provide it because it would be cumbersome to require those heavyweight mechanisms everywhere.

In the case of addWithOverflow that’s not redundant in the same way as its a matter of safety; + implicitly guards against overflow, while addWithOverflow() and &+ explicitly allow it. Regarding operators vs the methods they actually call, that’s sugar; the operator is just a shorter way to do the same thing, i.e- the operator is just a prettier way to write what is exactly the same code.

With the failable initialisers you’re using a distinctly separate feature to achieve the same end result that error handling is capable of, which is where I feel that the overlap produces redundancy.

> As I said, this does not guarantee better error handling if you're just throwing generic `InvalidParameterError`s. And if you *are* taking the time to precisely model all errors, that time may be better spent elsewhere.

That’s more an issue of developer laziness more than anything, but at least it’s still communicating that that’s exactly what the error is. If the standard library includes a good set of default errors then that should cover most use-cases; anything that isn’t covered by a standard error meanwhile absolutely should be given a new error type IMO.

> As an aside, I hope you realize that adding a human-readable string is *not helpful*. Without a machine-readable representation, the error cannot be expressed in domain-specific terms ("The ID number included an 'o'; did you mean '0'?") or even easily localized.

I’m not sure what you mean by this exactly; if you mean that an error should include extra data that can be extracted from the error instance itself then that depends on the error. If you’re capturing an invalid parameter error then it will be for debugging purposes I think, so the message is for the developer to inform them of which parameter failed (rather than having to have a different error for each parameter) and possibly some info on why, i.e- I’m not talking about text that would be spit out to a user or logged directly (except perhaps as a debug statement), or that you would expect to capture and process extensively. In this case even a simple string should be plenty helpful compared to nothing.


More information about the swift-evolution mailing list