[swift-evolution] Remove Failable Initializers

Haravikk swift-evolution at haravikk.me
Tue Mar 8 16:19:33 CST 2016


> On 8 Mar 2016, at 21:49, Austin Zheng <austinzheng at gmail.com> wrote:
> The more likely outcome is that people are going to wrap throwable initializers in factory methods returning optionals, and throw away whatever error returns.

I hope not, seeing as try? lets you explicitly ignore the error if you only need to know that an error occurred (vs what it actually was). The key difference here is that it’s explicit, and you have to choose how you’re going to handle it. This is one of the big arguments for error handling vs failable, as both model errors, but error handling forces you to be explicit about what you’re doing about them, even if it’s nothing.

> As for the sample code, there is absolutely nothing wrong with it IMO. The contract to me for 'asInt' reads "return an Int if an item exists at `index` and, if that item exists, it can be represented as an Int”.

That's an assumption; MyType actually expects to only contain valid numeric strings, and I’ve simply forgotten to add a force unwrap on the Int() initialiser, now suddenly my method is returning nil for indices that exist but were supplied incorrectly. With error handling I’m required to pick one of try, try? and try! to decide which suits my needs, in which case I’d put try!. The throws would also clarify that `nil` never indicates an error.

That said, I just realised it’s a poor example because the method shouldn’t return nil if it’s given an invalid index, rather it shouldn’t be given invalid indices in the first place (this would be in keeping with other methods in Array for example), but it’s just an over simplified example to make the point; the possible nil return from the initialiser is accidental.

> struct MyType {
>     let elements:[String]
> 
>     func asInt(index:Array<String>.Index) -> Int? {
>         if self.elements.indices.contains(index) {
>             return Int(self.elements[index])
>         }
>         return nil
>     }
> }

> I think it's instructional to look at how Dictionary<T?> works - if you try to extract an element you get a double optional. This isn't a type system bug, it's completely intentional. The internal optional indicates whether an extant element was Some or None, and the external optional indicates whether or not the element existed in the dictionary to begin with. If I were writing that `asInt` function and I really needed to make the distinction between "invalid index" and "not an integer string", I'd use either that same double optional pattern, or make the function throws. If I didn't care, I'd use the optional representation.

Double optional to me indicates a difference between a return value of “nothing” (didn’t exist) and “stored value was nil”, I’d be very wary about using that to replace errors.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160308/47272071/attachment.html>


More information about the swift-evolution mailing list