[Pitch] Introducing the "Unwrap or Die" operator to the standard library

From the user's standpoint, a crash is the worst thing possible, and should always be avoided. A failure doesn't need to be silent for user, the app can still communicate that there was an error with some kind of human readable message, and the developer can still be informed via any service that provides logging of non-fatal errors (there are many, most of them free).

During development, a crash can be informative, and that's what "assert" and things like that are for: even if I still prefer to not crash, and handle invariants by using specifically crafted types, I can understand the need for crashing in development, and from that standpoint I'd definitely support a proposal which goal is to make crashes caused by forced unwrapping more informative for the developer, or to force the developer to make them more informative (by substituting "!" with "!!").

The reason why I'm not completely convinced is the fact that there's already "fatalError", and its presence already clearly indicates in the code that something could trap there, in a verbally-appropriate way. In this sense a new operator could encourage practices that in my opinion should not be encouraged.

I understand your wish to never crash in production. But I think it’s an ideal that is quite far from the day to day development practices of many developers. If the philosophy of the Standard Library was to avoid all production crashes, we wouldn’t have the following:

Implicitly unwrapped optionals
Optional explicit unwrapping
fatalError
precondition

The fact is that we have those tools and that they are useful and used. I salute your wish to ban all of the above in an effort to avoid all crashes in production, but IMHO, it’s a style that is best enforced using a linter.

¡¡¡

On 29 Jun 2017, at 12:37, Elviro Rocca <retired.hunter.djura@gmail.com> wrote:

Elviro

Il giorno 29 giu 2017, alle ore 12:12, David Hart <david@hartbit.com <mailto:david@hartbit.com>> ha scritto:

On 29 Jun 2017, at 09:19, Elviro Rocca via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Il giorno 29 giu 2017, alle ore 03:18, Ben Cohen via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> ha scritto:

Finally, there’s a woolier justification: there’s an often-touted misconception out there that force unwraps are bad, that they were only created to accommodate legacy apps, and that force-unwrapping is always bad and you should never do it. This isn’t true – there are many good reasons to use force unwrap (though if you reaching for it constantly it’s a bad sign). Force-unwrapping is often better than just whacking in a default value or optional chaining when the presence of nil would indicate a serious failure. Introduction of the `!!` operator could help endorse/encourage the use of “thoughtful” force-unwrapping, which often comes with a comment of the reasoning why it’s safe (of why the array can’t be empty at this point, not just that it is empty). And if you’re going to write a comment, why not make that comment useful for debugging at the same time.

If one could still just "!" I'm not sure that the "!!" would really encourage a more thoughtful force unwrapping. Almost every crash related to a force unwrap that I see from Swift beginners is 100% due to the fact that adding and exclamation point makes the code compile, so they add it.

Also, I strongly disagree with your statement that the idea that force-unwraps are bad is a misconception: if something is Optional, there's a reason why it is, otherwise it would not be Optional at all, and that's the reason why Optional exists in Swift and represents a substantial technological advancement over Objective-C. Using an Optional means that we are actually adding a thoughtful information to an instance: it could be there, or it could not, and that's perfectly fine. Crashing an app in production for a force-unwrap results in the poorest user experience ever, and it should never happen.

If forced unwraps are only used in instances where you specifically expect the optional to not be nil, it is essentially sugar for a guard with preconditionFailure: it is used to enforce invariants the app should never break. It is very similar to a trap when accessing an out of bounds index in an array. In those cases, I actually prefer it crashing than having the app silently fail for the user, and you never finding out.

I try to limit my use of optional unwrapping but there are sometimes quite useful. IMHO, they are not inherently bad, but can be badly used.

I feel compelled to link another article, where Soroush Khanlou shows that sometimes the bare semantics of an Optional (that is, something is there or not) is not enough: Khanlou | That One Optional Property

I also disagree with the idea that the "?? fatalError()" alternative suffers from cognitive dissonance, for the following reasons:

- on the right of the "??" operator there could be both an Optional or a non-Optional, which would result in a different type for the resulting instance;
- fatalError() by itself is an operation that changes the meaning of a function, making it non-total, and its very name conveys a clear meaning to a person that reads the code, thus it really doesn't seem to me that "?? fatalError()" could be misinterpreted;

Anyway, it would be interesting to consider "!! message" as an alternative to "!", thus forcing the user to at least do extra effort: that could discourage a light use of !.

Elviro
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

My usage of “!!” generally falls in to two big buckets:

I've incorporated all the feedback to date and updated the gist:

bangbang.md ¡ GitHub

-- E

In my opinion the phrase "An often-touted misconception ... a serious failure." taken from Ben Cohen's comment should not be included in the proposal because, other than being a personal opinion, it's a extremely generic statement that doesn't suggest at all "when" force unwrap is good and it's not useful as a guideline for people that read this things to learn something. Saying things like "this is kind of ok but too much of it is wrong" is not useful.

// in a custom view controller subclass that only accepts children of a certain kind:
let existing = childViewControllers as? Array<TableRowViewController> !! "TableViewController must only have TableRowViewControllers as children"

In my opinion this example shows very well how you can always switch the need to force unwrap with a more specific contract for a class, or in other words, with a more specific type. Instead of referring to childViewControllers, you could simply have an array of objects of the specific type that mediates the addition and reference to the underlying "childViewControllers" storage. It uses the type system to enforce correctness.

This is getting off on a tangent, but the interface I wrote for my TableViewController does that.

However

Since it is a subclass of NS/UIViewController, the `childViewControllers` property is public and directly mutable by anyone. Thus, the assertion that the class is not being misused.

Dave

¡¡¡

On Jun 29, 2017, at 12:23 PM, Djura Retired Hunter via swift-evolution <swift-evolution@swift.org> wrote:

Il giorno 29 giu 2017, alle ore 19:05, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> ha scritto:

On Jun 29, 2017, at 9:13 AM, Dave DeLong <delong@apple.com <mailto:delong@apple.com>> wrote:

Now, I'm NOT saying that force unwrapping is always bad, but adding examples like these to a official proposal could make people think that force unwrapping is a perfectly fine thing to do for production code instead of designing types for safety, and safety enforced with a strong type system IS a cornerstone of Swift, like it or not, as reported on swift.org/about: <http://swift.org/about:&gt; "The most obvious way to write code should also behave in a safe manner. Undefined behavior is the enemy of safety, and developer mistakes should be caught before software is in production. Opting for safety sometimes means Swift will feel strict, but we believe that clarity saves time in the long run.".

Elviro
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

As much as I favor Swift's preference for API contracts, "I would just
write the code differently," is missing the point of this suggestion.
You can design a single function to look beautiful without any unwraps,
but in a system or a codebase, you must check those preconditions at
some point. If you are dealing with other paradigms that you can't
control, like Cocoa two-stage loading, you must check those
preconditions at some point.
Zach Waldowski
zach@waldowski.me

¡¡¡

On Thu, Jun 29, 2017, at 01:43 PM, ilya via swift-evolution wrote:

I'm not sure those examples are what we should aim for. Generally I
try to avoid force unwrapping, and specifically in these cases I
belive a more reliable code would be produced without either ! or the
!! operators:>
bangbang.md ¡ GitHub

I understand coding styles may differ, but I do worry that we
"promote" force unwrapping too much by inroducing another
operator for it.>
Ilya.

On Thu, Jun 29, 2017 at 6:55 PM Erica Sadun via swift-evolution <swift- > evolution@swift.org> wrote:>>

On Jun 29, 2017, at 9:13 AM, Dave DeLong <delong@apple.com> wrote:

My usage of “!!” generally falls in to two big buckets:

I've incorporated all the feedback to date and updated the gist:

bangbang.md ¡ GitHub

-- E

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_________________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Fixed. Finger slipped during spell correct and I thought I had backed up through enough items -- apparently I didn't. :(

-- E

¡¡¡

On Jun 29, 2017, at 2:29 PM, Rod Brown <rodney.brown6@icloud.com> wrote:

Thanks Erica. That looks great.

As a side-note, one of the sentences in “The Black Swan Deployment” references a “Mackintosh” - I believe this was supposed to be “Hackintosh”.

On 30 Jun 2017, at 3:20 am, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jun 29, 2017, at 9:13 AM, Dave DeLong <delong@apple.com <mailto:delong@apple.com>> wrote:

My usage of “!!” generally falls in to two big buckets:

I've incorporated all the feedback to date and updated the gist:

bangbang.md ¡ GitHub

-- E

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Well, a persisted inconsistency is worse than a crash :P

I still strongly disagree with this, I'm sorry. Inconsistent data is an orthogonal problem to fatal errors, and a crash is basically a middle finger to the user.

To me this !! operator does not add enough value to put it in the standard library. The existing force unwrap operator already works fine, and if Never is really going to be a bottom type, then I don't know in which cases the !! operator would be better.

Actually, for me it would be worse, because you could only call it with a String or a function that returned a String. What happens if I want to call a method that does some housekeeping and then ends with a fatalError? I could not use the !! operator unless that method returns a String.

And finally, I would not want to find such code in a project with multiple people involved, as my personal experience is that a single ! leads to crashes in production sooner or later, and if you track such crashes it would not be obvious why they happen. If that code is written by one person and will not be published anywhere, I suppose you can use it, but either in open source projects or in teams, I would avoid that operator like hell.

Instead of writing this (the original example, which by the way has a typo :P):

guard !lastItem.isEmpty else { return }
let lastItem = array.last !! "Array must be non-empty"

I would just write this:

guard let lastItem = array.last else { return }

The first example seems reasonable enough: you know that the array is empty, you just checked for it! But that array may possible be a property of a class and it can be changed in another thread, so you can't be so sure. And because software is usually never finished, some other team mate can add more code between the first line and the second line, making it easier to inadvertently change the array contents and thus not preserving the initial invariants. The second example may need to be rewritten if the requirements change, but it does not have the same drawbacks as the initial example and, well, it's definitely less code and easier to read.

Anyway, can we provide a real world example? If some projects are already defining this operator, working examples must exist and we can analyze how this operator affects the code and which patterns are leveraged by it.

I agree with most of this, but I think the contraposition here is with:

guard let lastItem = array.last else { fatalError() }

I understand your wish to never crash in production. But I think it’s an ideal that is quite far from the day to day development practices of many developers. If the philosophy of the Standard Library was to avoid all production crashes, we wouldn’t have the following:

Implicitly unwrapped optionals
Optional explicit unwrapping
fatalError
precondition

The fact is that we have those tools and that they are useful and used. I salute your wish to ban all of the above in an effort to avoid all crashes in production, but IMHO, it’s a style that is best enforced using a linter.

Swift is a language that doesn't enforce a particular style over the other (thankfully) so the standard library includes many constructs that can end up being mostly ignored by some people in some projects.

Because of that, I don't think that we should add to the standard library a particular construct just because some people use it in third party libraries, if the same behavior can be already accomplished with very minimal code using what's already there.

Also, avoiding all crashes in production is to me a fundamental value of software development, and is not particularly related to Swift or iOS or whatever. In fact, as was already written some posts before, logic errors are either mistakes or are used to avoid additional checks in cases when the overhead introduced by those checks is not acceptable from a performance standpoint, which is a very unfrequent case.

Now, during development I can decide to make something crash for informative reasons, so in general I'm not opposed at all to those constructs, but that doesn't justify production code that crashes due to implicit unwrapping or out-of-bounds errors. That's my opinion, and it's also my opinion that trusting a linter is a much weaker solution than actually enforcing the type-system and the compiler, and I'd say that Swift is not Objective-C or PHP, but that's a completely different topic.

Elviro