[swift-evolution] Remove Failable Initializers
swift-evolution at haravikk.me
Fri Mar 4 03:54:52 CST 2016
> On 3 Mar 2016, at 22:27, Brent Royal-Gordon <brent at architechies.com> wrote:
>>> The guidance is that returning nil is appropriate when your function could fail due to simple domain errors, for instance passing a negative value as a parameter where they aren't allowed.
>> If that’s all you need then what’s wrong with throwing some generic purpose illegal argument type exception and using try? It’s functionally the same, i.e- removing failable initialisers doesn’t actually remove a capability, it just removes what is essentially now a redundant one. In other words, try? on a throwable initialiser is now functionally identical to a failable initialiser and isn't appreciably more complex, especially if there’s a common exception that can be thrown for general purpose invalid argument errors (not sure if there is one right now, but it could be added to the proposal).
> If there's a "general purpose invalid argument error", then that error is not communicating anything more than an optional would. It's merely taking a lot more syntax on both sides of the call, plus complexity in the calling convention and space in memory (40 bytes for a protocol witness, instead of 0-1 bytes for the overhead of an Optional!), to convey the same single bit of information: "Your data was wrong”.
That’s more an issue of optimisation IMO; if an initialiser (or method) is being called with try? or try! then it should be possible to optimise away the actual error generation and throwing, since it won’t be used anyway. I don’t know enough about that to know what’s currently being done in these cases, but with optimisations in place the only added complexity should be picking a relevant error to throw and then throwing it.*
Also, I’m not convinced by the idea that failable initialisers always represent only a single type of error anyway; the most common example I’ve seen is people using NSImage, but that can actually fail for several different reasons, such as a non-existent file, or the file is unreadable due to permission issues and such. While you could argue that these all stem from the parameter choice being wrong, you can argue that about a lot of things. I’d say in that case at least there should be multiple different error types for different potential types of failure, otherwise you’re left having to figure out why the issue occurred for yourself; consider for example if the file being opened were a temporary file created with the wrong permissions, it may be deleted if execution halts, leaving you only with the knowledge that something failed in NSImage(), which is why I think it’s better practice to use error handling in general as you can instead catch that error to find out what is actually wrong.
This can be true even in the supposedly simple cases; consider a simple type that is initialised from a numeric value in string format. You might say that that’s fine as an optional, but what if the string fails simply because it contains a single space character? This could be pretty non-obvious, and with most failable initialisers you’re left with nothing other than “your data was wrong” (was it malformed, did it contain invalid characters, is the value too large or too small?); this can be even worse if you include other unusual unicode characters that could look valid. If this initialiser instead returned an error type indicating that it encountered a non-numeric string, then it would leave no doubt where the error lies; granted using try? you won’t see this, but you have the option of catching the error instead to find out what it was*.
Of course adding new error types is extra work, but that’s partly an argument for the standard library to contain a good range of general purpose types, which would likely have to be added anyway while replacing failable initialisers. Even if developers fall back to a vague “InvalidParameter” error type, they could at least provide a message with it, which is still more information which is IMO always a good thing.
*Just a note, but ideally when debugging no optimisation of errors should occur, as this would give us the option of finding out what causes a try? statement to fail; this may even already be possible, I’m not sure.
More information about the swift-evolution