[Discussion]: Deprecate !-Unwrapping of Optionals


(Robert Widmann) #1

Optional values pervade Swift code and so there is a significant part of the language dedicated to manipulating them and using them safely. An optional value may be conditionally let-bound, guarded, pattern-matched on, given a default value with ??, or used with higher-order functions like map and flatMap without having to interact with partiality. A corner case, however, remains in the form of the postfix-! force-unwrap operator. At the term level, there is little reason to unwrap given the syntactic constructs above, and indeed code that does becomes brittle and, by its very nature, open to the dreaded "unexpectedly found nil while unwrapping an optional value" error (our very own NullPointerException). In addition, bang is incredibly hard to spot in code bases that use it because it blends in with Boolean negation (which, in itself, is notoriously hard to spot in other C-like-languages), harming readability and making code review more difficult. Finally, if the authors of a piece of code have such confidence in the existence of a value that they feel it safe to use force unwrapping, they should simply use a non-optional value and cut out a level of indirection and avoid the dangers of bang in the first place.

Because of the above, I'd like to start a discussion about its deprecation and eventual removal in favor of either an unsafeUnwrap function or other extra-syntactic constructs. I don't intend for force-unwrapping as a concept to go away, as there will always be corner cases where it is necessary, but I don't believe it deserves to be so easy to do in a language that holds safety as a core tenant.

Thanks all,

~Robert Widmann


(Andrew Clissold) #2

I absolutely agree that force-unwrapping is ugly and dangerous, but I think it's still strongly needed for convenience when working with Objective-C APIs that haven't been audited for nullability. Of which there are still a very large amount :slight_smile:

No new Swift code should make use of force unwrapping, but that should remain a developer convention rather than a compiler requirement for the time being.

Andrew

···

On Feb 28, 2016, at 11:53 AM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

Optional values pervade Swift code and so there is a significant part of the language dedicated to manipulating them and using them safely. An optional value may be conditionally let-bound, guarded, pattern-matched on, given a default value with ??, or used with higher-order functions like map and flatMap without having to interact with partiality. A corner case, however, remains in the form of the postfix-! force-unwrap operator. At the term level, there is little reason to unwrap given the syntactic constructs above, and indeed code that does becomes brittle and, by its very nature, open to the dreaded "unexpectedly found nil while unwrapping an optional value" error (our very own NullPointerException). In addition, bang is incredibly hard to spot in code bases that use it because it blends in with Boolean negation (which, in itself, is notoriously hard to spot in other C-like-languages), harming readability and making code review more difficult. Finally, if the authors of a piece of code have such confidence in the existence of a value that they feel it safe to use force unwrapping, they should simply use a non-optional value and cut out a level of indirection and avoid the dangers of bang in the first place.

Because of the above, I'd like to start a discussion about its deprecation and eventual removal in favor of either an unsafeUnwrap function or other extra-syntactic constructs. I don't intend for force-unwrapping as a concept to go away, as there will always be corner cases where it is necessary, but I don't believe it deserves to be so easy to do in a language that holds safety as a core tenant.

Thanks all,

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


(Brent Royal-Gordon) #3

Because of the above, I'd like to start a discussion about its deprecation and eventual removal in favor of either an unsafeUnwrap function or other extra-syntactic constructs. I don't intend for force-unwrapping as a concept to go away, as there will always be corner cases where it is necessary, but I don't believe it deserves to be so easy to do in a language that holds safety as a core tenant.

I could not disagree more strongly.

Swift takes the position that assertions are good and that it's better to crash in an unexpected state than to limp on. Force unwrapping is an example of that philosophy. A force unwrap is a "this is not nil" assertion.

That assertion often is not appropriate—namely, in code that tests for `nil`—and so we provide, by my count, five general ways to test-and-use nils (pattern matching, optional binding, optional chaining, defaulting, and mapping) without force unwrapping. These features are all great and, with *very* few exceptions, you should always use one of them rather than test-and-force-unwrap.

But where it *is* appropriate to assert non-nil, *we want you to force unwrap*. If, as you write your code, you are certain a value cannot be nil, we want your code to say so. That way, the code will crash if you're wrong, at the exact point where you made the mistake, where the problem will be easiest to diagnose. We do *not* want you to use a test-and-use construct in a place where you believe the test will always succeed, because you will probably not give any particular attention to the test-failed behavior, so it will probably be wrong.

In other words, making force unwrapping more difficult will not make people correctly handle unexpected nils—it will make them write code that *undetectably* does the wrong thing by skipping code that wasn't actually meant to be skipped. You'll see fewer crashes but more bugs. That runs counter to Swift's formulation of safety, and I think it would be a serious mistake.

I recently had the pleasure of debugging some force unwrap crashes in a view controller I hadn't touched in a while. The force unwraps I used very quickly demonstrated to me that my ideas about the code's lifecycle were wrong: for instance, I thought it would never be called before a certain property was set, but it turned out it sometimes was. If I had used a conditional construct without thinking through the consequences, that would have manifested as a mysteriously empty menu which I would have had trouble debugging. By stating explicitly in my code that I believed those values could not be nil, Swift quickly disproved my belief, showed me exactly what was wrong, and prompted me to correct it. That's the best thing it could have done for me in this situation.

I often see Swift style guides suggest that force-unwrapping is a strong code smell and should always be avoided, but I think that's wrongheaded. Test-and-force-unwrap *is* a code smell, and force-unwrap-when-you're-not-really-sure is incorrect too. But when you *are* sure, force unwrap is the very best way to write your code, because it will make any mistakes immediately obvious.

That's a feature, not a bug.

···

--
Brent Royal-Gordon
Architechies


(Robert Widmann) #4

I have to clarify here: I am not taking about .!, as!, as?, ?, etc. I am taking about making naked bang-unwrapping more difficult. That is, those cases when you write

let thing = optVal!

These are equivalent to naked unguarded dereferences in C-like languages and are, we can all agree, a code smell, not a feature. One would not ever recommend to a user of such languages that * or -> be used before checking that an object or region of memory was non-NULL. So, too, should we discourage this case by removing postfix-! from the language itself (notice it isn't even an operator, it is actually a part of the language).

~Robert Widmann

2016/02/28 14:53、Developer <devteam.codafi@gmail.com> のメッセージ:

···

Optional values pervade Swift code and so there is a significant part of the language dedicated to manipulating them and using them safely. An optional value may be conditionally let-bound, guarded, pattern-matched on, given a default value with ??, or used with higher-order functions like map and flatMap without having to interact with partiality. A corner case, however, remains in the form of the postfix-! force-unwrap operator. At the term level, there is little reason to unwrap given the syntactic constructs above, and indeed code that does becomes brittle and, by its very nature, open to the dreaded "unexpectedly found nil while unwrapping an optional value" error (our very own NullPointerException). In addition, bang is incredibly hard to spot in code bases that use it because it blends in with Boolean negation (which, in itself, is notoriously hard to spot in other C-like-languages), harming readability and making code review more difficult. Finally, if the authors of a piece of code have such confidence in the existence of a value that they feel it safe to use force unwrapping, they should simply use a non-optional value and cut out a level of indirection and avoid the dangers of bang in the first place.

Because of the above, I'd like to start a discussion about its deprecation and eventual removal in favor of either an unsafeUnwrap function or other extra-syntactic constructs. I don't intend for force-unwrapping as a concept to go away, as there will always be corner cases where it is necessary, but I don't believe it deserves to be so easy to do in a language that holds safety as a core tenant.

Thanks all,

~Robert Widmann


(Joseph Lord) #5

As you point out there are there are circumstances where force unwrapping is required. Off the top of my head:

1) Implementation of flatMap and other similar safe functions requires it or similar.
2) Performance - in hot loops the conditionality may be a real performance penalty and other checks or outside knowledge may let you know that it is safe.
3) Some people prefer to assert and crash in production and this is a valid option.

Personally I only use the force unwrapping in test code where crashes are just untidy failures if they fail.

If it is removed from the stdlib it could be implemented as a custom operator by those that want it but there would be a performance hit for calls from other modules as I don't think it could currently be inlined.

I'm fairly neutral about whether this happens.

Joseph

···

On Feb 28, 2016, at 7:53 PM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

Optional values pervade Swift code and so there is a significant part of the language dedicated to manipulating them and using them safely. An optional value may be conditionally let-bound, guarded, pattern-matched on, given a default value with ??, or used with higher-order functions like map and flatMap without having to interact with partiality. A corner case, however, remains in the form of the postfix-! force-unwrap operator. At the term level, there is little reason to unwrap given the syntactic constructs above, and indeed code that does becomes brittle and, by its very nature, open to the dreaded "unexpectedly found nil while unwrapping an optional value" error (our very own NullPointerException). In addition, bang is incredibly hard to spot in code bases that use it because it blends in with Boolean negation (which, in itself, is notoriously hard to spot in other C-like-languages), harming readability and making code review more difficult. Finally, if the authors of a piece of code have such confidence in the existence of a value that they feel it safe to use force unwrapping, they should simply use a non-optional value and cut out a level of indirection and avoid the dangers of bang in the first place.

Because of the above, I'd like to start a discussion about its deprecation and eventual removal in favor of either an unsafeUnwrap function or other extra-syntactic constructs. I don't intend for force-unwrapping as a concept to go away, as there will always be corner cases where it is necessary, but I don't believe it deserves to be so easy to do in a language that holds safety as a core tenant.

Thanks all,

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


(Kenny Leung) #6

I strongly agree with this. The main goal of optionals is to help eliminate null pointer exceptions, yet there is a common mechanism to put them back into your code. This doesn’t make much sense to me.

Not having forced unwrapping makes you take care of the cases where you might run into null, and will push the language in a direction where dealing with the null is just as easy as risking a null pointer exception is today.

-Kenny

···

On Feb 28, 2016, at 11:53 AM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

Optional values pervade Swift code and so there is a significant part of the language dedicated to manipulating them and using them safely. An optional value may be conditionally let-bound, guarded, pattern-matched on, given a default value with ??, or used with higher-order functions like map and flatMap without having to interact with partiality. A corner case, however, remains in the form of the postfix-! force-unwrap operator. At the term level, there is little reason to unwrap given the syntactic constructs above, and indeed code that does becomes brittle and, by its very nature, open to the dreaded "unexpectedly found nil while unwrapping an optional value" error (our very own NullPointerException). In addition, bang is incredibly hard to spot in code bases that use it because it blends in with Boolean negation (which, in itself, is notoriously hard to spot in other C-like-languages), harming readability and making code review more difficult. Finally, if the authors of a piece of code have such confidence in the existence of a value that they feel it safe to use force unwrapping, they should simply use a non-optional value and cut out a level of indirection and avoid the dangers of bang in the first place.

Because of the above, I'd like to start a discussion about its deprecation and eventual removal in favor of either an unsafeUnwrap function or other extra-syntactic constructs. I don't intend for force-unwrapping as a concept to go away, as there will always be corner cases where it is necessary, but I don't believe it deserves to be so easy to do in a language that holds safety as a core tenant.

Thanks all,

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


(Joel Lopes Da Silva) #7

I’m not sure about deprecating force unwrapping altogether; it might be too early for that.

But I think it would be a good idea to create a new opt-in compiler warning flag that would point to any code that relies on force unwrapping (whether explicitly, or with implicitly wrapped optionals).

···

--
Joel Lopes Da Silva

On Feb 28, 2016, at 11:53 AM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

Optional values pervade Swift code and so there is a significant part of the language dedicated to manipulating them and using them safely. An optional value may be conditionally let-bound, guarded, pattern-matched on, given a default value with ??, or used with higher-order functions like map and flatMap without having to interact with partiality. A corner case, however, remains in the form of the postfix-! force-unwrap operator. At the term level, there is little reason to unwrap given the syntactic constructs above, and indeed code that does becomes brittle and, by its very nature, open to the dreaded "unexpectedly found nil while unwrapping an optional value" error (our very own NullPointerException). In addition, bang is incredibly hard to spot in code bases that use it because it blends in with Boolean negation (which, in itself, is notoriously hard to spot in other C-like-languages), harming readability and making code review more difficult. Finally, if the authors of a piece of code have such confidence in the existence of a value that they feel it safe to use force unwrapping, they should simply use a non-optional value and cut out a level of indirection and avoid the dangers of bang in the first place.

Because of the above, I'd like to start a discussion about its deprecation and eventual removal in favor of either an unsafeUnwrap function or other extra-syntactic constructs. I don't intend for force-unwrapping as a concept to go away, as there will always be corner cases where it is necessary, but I don't believe it deserves to be so easy to do in a language that holds safety as a core tenant.

Thanks all,

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


(William Dillon) #8

I’m not sure about deprecating force unwrapping altogether; it might be too early for that.

But I think it would be a good idea to create a new opt-in compiler warning flag that would point to any code that relies on force unwrapping (whether explicitly, or with implicitly wrapped optionals).

···

On February 29, 2016 at 12:31:46 PM, Joel Lopes Da Silva via swift-evolution (swift-evolution@swift.org) wrote:

--
Joel Lopes Da Silva

I agree strongly with this point.

- Will


(Radek Pietruszewski) #9

So:

a) I agree force-unwrapping is often overused and abused, especially by Swift beginners
b) I also agree with Brent that it’s a useful feature, and while not terribly often used, by me at least, important to have

Given that, I agree that having a simple to use `!` postfix operator might push people to abuse it. If it’s so simple to use, it means it’s important and should be used often. If it was harder to reach, it wouldn’t be used as often, because it would be relatively easier to do the right thing.

But. While `!` is perhaps too easy to type, it’s also very easy to spot while looking at code. It shouts at you “hey, do you really need me here?!”. `unsafeUnwrap` is harder to spot.

Also: `!` can be chained nicely like so:

  5> struct A {
  6. var x: Int
  7. }
  8> struct B {
  9. var a: A?
10. }
11> var a = B(a: A(x: 10))
12> a.a!.x = 20

You can’t do that with a function or a method. Though you could do it with a property, I think, like `a.a.forceUnwrapped.x = 20`.

— Radek

···

On 28 Feb 2016, at 20:53, Developer via swift-evolution <swift-evolution@swift.org> wrote:

Optional values pervade Swift code and so there is a significant part of the language dedicated to manipulating them and using them safely. An optional value may be conditionally let-bound, guarded, pattern-matched on, given a default value with ??, or used with higher-order functions like map and flatMap without having to interact with partiality. A corner case, however, remains in the form of the postfix-! force-unwrap operator. At the term level, there is little reason to unwrap given the syntactic constructs above, and indeed code that does becomes brittle and, by its very nature, open to the dreaded "unexpectedly found nil while unwrapping an optional value" error (our very own NullPointerException). In addition, bang is incredibly hard to spot in code bases that use it because it blends in with Boolean negation (which, in itself, is notoriously hard to spot in other C-like-languages), harming readability and making code review more difficult. Finally, if the authors of a piece of code have such confidence in the existence of a value that they feel it safe to use force unwrapping, they should simply use a non-optional value and cut out a level of indirection and avoid the dangers of bang in the first place.

Because of the above, I'd like to start a discussion about its deprecation and eventual removal in favor of either an unsafeUnwrap function or other extra-syntactic constructs. I don't intend for force-unwrapping as a concept to go away, as there will always be corner cases where it is necessary, but I don't believe it deserves to be so easy to do in a language that holds safety as a core tenant.

Thanks all,

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


(Dave Abrahams) #10

Whether ! is a problem depends how you use it. IIUC it's very common
that you know an optional is non-nil unless there's a bug in your code.
In those cases, IMO, y! is far preferable in terms of maintainability to

  if let x = y {

  }
  else fatalError("...")

and in terms of both maintainability *and* reliability, is much better
than:

  if let x = y {

  }
  else {
     // supposed "recovery" code that never actually gets executed
  }

If people use y! to avoid dealing with cases that can actually arise in
correct code that's a problem, but I'm not sure taking away "y!" is the
cure.

···

on Sun Feb 28 2016, Developer <swift-evolution@swift.org> wrote:

Optional values pervade Swift code and so there is a significant part
of the language dedicated to manipulating them and using them safely.
An optional value may be conditionally let-bound, guarded,
pattern-matched on, given a default value with ??, or used with
higher-order functions like map and flatMap without having to interact
with partiality. A corner case, however, remains in the form of the
postfix-! force-unwrap operator. At the term level, there is little
reason to unwrap given the syntactic constructs above, and indeed code
that does becomes brittle and, by its very nature, open to the dreaded
"unexpectedly found nil while unwrapping an optional value" error (our
very own NullPointerException).

--
-Dave


(Haravikk) #11

There are some cases where it’s still very useful even in pure Swift code. For example, say I have an array of optional values, and want to trip nils, then perform a transform on the result, I would do something like:

  let theResult = myArrayOfOptions.filter({ $0 != nil }).map({ doSomethingTo($0!) })

I could use if let inside the second closure, but it’d be messy; in fact, both that and using the ?? operator would require me to provide some kind of default, which isn’t possible if I have elements of arbitrary type. I could create a removeNils() method that returns a non-optional array (or lazy collection) but I’m not actually sure how to do that, and it’d definitely be more work than just using the exclamation point to tell the compiler “actually there are no optionals left at this point”.

···

On 29 Feb 2016, at 02:54, Andrew Clissold via swift-evolution <swift-evolution@swift.org> wrote:

I absolutely agree that force-unwrapping is ugly and dangerous, but I think it's still strongly needed for convenience when working with Objective-C APIs that haven't been audited for nullability. Of which there are still a very large amount :slight_smile:

No new Swift code should make use of force unwrapping, but that should remain a developer convention rather than a compiler requirement for the time being.

Andrew

On Feb 28, 2016, at 11:53 AM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

Optional values pervade Swift code and so there is a significant part of the language dedicated to manipulating them and using them safely. An optional value may be conditionally let-bound, guarded, pattern-matched on, given a default value with ??, or used with higher-order functions like map and flatMap without having to interact with partiality. A corner case, however, remains in the form of the postfix-! force-unwrap operator. At the term level, there is little reason to unwrap given the syntactic constructs above, and indeed code that does becomes brittle and, by its very nature, open to the dreaded "unexpectedly found nil while unwrapping an optional value" error (our very own NullPointerException). In addition, bang is incredibly hard to spot in code bases that use it because it blends in with Boolean negation (which, in itself, is notoriously hard to spot in other C-like-languages), harming readability and making code review more difficult. Finally, if the authors of a piece of code have such confidence in the existence of a value that they feel it safe to use force unwrapping, they should simply use a non-optional value and cut out a level of indirection and avoid the dangers of bang in the first place.

Because of the above, I'd like to start a discussion about its deprecation and eventual removal in favor of either an unsafeUnwrap function or other extra-syntactic constructs. I don't intend for force-unwrapping as a concept to go away, as there will always be corner cases where it is necessary, but I don't believe it deserves to be so easy to do in a language that holds safety as a core tenant.


(Robert Widmann) #12

That's certainly an interesting stance to take on the issue. I recently went through a rather large refactoring myself involving mechanically-ported code that had hundreds of force-unwraps left over from Objective-C. It delighted me to no end to spend days removing them and refactoring the code. To my mind, each represented an opportunity to explore an alternative means of expressing something very old hat because I would rather not deal with partiality in any way, shape, or form if I didn't have to. In the end, it was a tough refactoring job because ! was so hard to spot and was a pain to wrestle out of the codebase in some spots. By the time I merged, there were still spots where it was necessary to use !, but I ended up removing and refactoring hundreds of instances of improperly applied "assumptions" about my code. Bang, as you said, showed me these cases as false preconditions, but I guess I can't understand why that would be a desirable feature when compared to simply writing the code with other primitives I listed. Especially because I was working with a library written entirely with objects, what "assertions" we're left still make me incredibly uneasy.

Yes, occasionally you will get cleaner code just making the assertion that something is non-nil, but does that occasion necessitate language-level syntax rather than a stdlib function? Why not have an unwrap function over an unwrap language feature [because surely the wide range of assertion operators already present in stdlib could use a new counterpart]?

~Robert Widmann

2016/02/29 3:40、Brent Royal-Gordon <brent@architechies.com> のメッセージ:

···

Because of the above, I'd like to start a discussion about its deprecation and eventual removal in favor of either an unsafeUnwrap function or other extra-syntactic constructs. I don't intend for force-unwrapping as a concept to go away, as there will always be corner cases where it is necessary, but I don't believe it deserves to be so easy to do in a language that holds safety as a core tenant.

I could not disagree more strongly.

Swift takes the position that assertions are good and that it's better to crash in an unexpected state than to limp on. Force unwrapping is an example of that philosophy. A force unwrap is a "this is not nil" assertion.

That assertion often is not appropriate—namely, in code that tests for `nil`—and so we provide, by my count, five general ways to test-and-use nils (pattern matching, optional binding, optional chaining, defaulting, and mapping) without force unwrapping. These features are all great and, with *very* few exceptions, you should always use one of them rather than test-and-force-unwrap.

But where it *is* appropriate to assert non-nil, *we want you to force unwrap*. If, as you write your code, you are certain a value cannot be nil, we want your code to say so. That way, the code will crash if you're wrong, at the exact point where you made the mistake, where the problem will be easiest to diagnose. We do *not* want you to use a test-and-use construct in a place where you believe the test will always succeed, because you will probably not give any particular attention to the test-failed behavior, so it will probably be wrong.

In other words, making force unwrapping more difficult will not make people correctly handle unexpected nils—it will make them write code that *undetectably* does the wrong thing by skipping code that wasn't actually meant to be skipped. You'll see fewer crashes but more bugs. That runs counter to Swift's formulation of safety, and I think it would be a serious mistake.

I recently had the pleasure of debugging some force unwrap crashes in a view controller I hadn't touched in a while. The force unwraps I used very quickly demonstrated to me that my ideas about the code's lifecycle were wrong: for instance, I thought it would never be called before a certain property was set, but it turned out it sometimes was. If I had used a conditional construct without thinking through the consequences, that would have manifested as a mysteriously empty menu which I would have had trouble debugging. By stating explicitly in my code that I believed those values could not be nil, Swift quickly disproved my belief, showed me exactly what was wrong, and prompted me to correct it. That's the best thing it could have done for me in this situation.

I often see Swift style guides suggest that force-unwrapping is a strong code smell and should always be avoided, but I think that's wrongheaded. Test-and-force-unwrap *is* a code smell, and force-unwrap-when-you're-not-really-sure is incorrect too. But when you *are* sure, force unwrap is the very best way to write your code, because it will make any mistakes immediately obvious.

That's a feature, not a bug.

--
Brent Royal-Gordon
Architechies


(David Owens II) #13

I don't agree with this assertion. There are _many_ cases where you don't check for the pointer being null before accessing it. Those are programmer errors when the data is null. If there is no graceful way for the program to handle that case, what's the point of silently failing? Maybe later you come along because something keeps coming up with a null problem that you can't track down so you build something around it to recover.

I also don't see `let thing = unwrap(optVal)` or `let thing = optVal.unwrap()` as significantly better. The functionality still needs to be there, and this version is only slightly easier to grep vs. !.

-David

···

On Feb 29, 2016, at 6:54 AM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

I have to clarify here: I am not taking about .!, as!, as?, ?, etc. I am taking about making naked bang-unwrapping more difficult. That is, those cases when you write

let thing = optVal!

These are equivalent to naked unguarded dereferences in C-like languages and are, we can all agree, a code smell, not a feature. One would not ever recommend to a user of such languages that * or -> be used before checking that an object or region of memory was non-NULL. So, too, should we discourage this case by removing postfix-! from the language itself (notice it isn't even an operator, it is actually a part of the language).


(Seth Friedman) #14

I think Brent beautifully captured opinions I share on force unwrapping. It
has proven useful time and time again in helping me identify incorrect
assumptions I've made.

Strong -1 to this proposal

Thanks,
Seth

···

On Mon, Feb 29, 2016 at 2:43 AM Joseph Lord via swift-evolution < swift-evolution@swift.org> wrote:

As you point out there are there are circumstances where force unwrapping
is required. Off the top of my head:

1) Implementation of flatMap and other similar safe functions requires it
or similar.
2) Performance - in hot loops the conditionality may be a real performance
penalty and other checks or outside knowledge may let you know that it is
safe.
3) Some people prefer to assert and crash in production and this is a
valid option.

Personally I only use the force unwrapping in test code where crashes are
just untidy failures if they fail.

If it is removed from the stdlib it could be implemented as a custom
operator by those that want it but there would be a performance hit for
calls from other modules as I don't think it could currently be inlined.

I'm fairly neutral about whether this happens.

Joseph

> On Feb 28, 2016, at 7:53 PM, Developer via swift-evolution < > swift-evolution@swift.org> wrote:
>
> Optional values pervade Swift code and so there is a significant part of
the language dedicated to manipulating them and using them safely. An
optional value may be conditionally let-bound, guarded, pattern-matched on,
given a default value with ??, or used with higher-order functions like map
and flatMap without having to interact with partiality. A corner case,
however, remains in the form of the postfix-! force-unwrap operator. At
the term level, there is little reason to unwrap given the syntactic
constructs above, and indeed code that does becomes brittle and, by its
very nature, open to the dreaded "unexpectedly found nil while unwrapping
an optional value" error (our very own NullPointerException). In addition,
bang is incredibly hard to spot in code bases that use it because it blends
in with Boolean negation (which, in itself, is notoriously hard to spot in
other C-like-languages), harming readability and making code review more
difficult. Finally, if the authors of a piece of code have such confidence
in the existence of a value that they feel it safe to use force unwrapping,
they should simply use a non-optional value and cut out a level of
indirection and avoid the dangers of bang in the first place.
>
> Because of the above, I'd like to start a discussion about its
deprecation and eventual removal in favor of either an unsafeUnwrap
function or other extra-syntactic constructs. I don't intend for
force-unwrapping as a concept to go away, as there will always be corner
cases where it is necessary, but I don't believe it deserves to be so easy
to do in a language that holds safety as a core tenant.
>
> Thanks all,
>
> ~Robert Widmann
> _______________________________________________
> 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


(Pyry Jahkola) #15

+(0.5 + 0.5). I think both of you have a point.

To me, even after soon two years of Swift, it's still extremely hard to visually see where forced unwrapping happens. I do not think we should recommend unsafeUnwrap though because we'd then trade a guaranteed crash for undefined behaviour on nil, which is definitely the wrong choice at least 95% of time.

Unless we're getting an option in Xcode to make the exclamation point U+0021 render as something like :exclamation:️ (U+2757 U+FE0F), I think it would be more sensible to rather have a standard member function on Optional that does the same but more explicitly:

extension Optional {
  func unwrap() -> Wrapped {
    guard let value = self else {
      fatalError("unexpectedly found nil where unwrapping an \(Optional.self) value")
    }
    return value
  }
}

var x: String?
if x != nil { print(x.unwrap()) }

Indeed, with Optional.unwrap as a member function, we could even report where the assertion failed, which could be a big help when debugging errors that slipped into production:

extension Optional {
  func unwrap(file file: StaticString = #file, line: UInt = #line) -> Wrapped { ... }
}

print(x.unwrap())
fatal error: unexpectedly found nil where unwrapping an Optional<String> value: file /path/to/endless/swift-evolution/this-email-message.swift, line 36

There could be compiler magic to replace calls to Optional.unwrap with unsafeUnwrap in -Ounchecked builds.

Naming alternatives considered: forceUnwrap, forcedUnwrap, unwrapValue, assertUnwrap.

— Pyry

···

On 29 Feb 2016, at 10:40, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

Because of the above, I'd like to start a discussion about its deprecation and eventual removal in favor of either an unsafeUnwrap function or other extra-syntactic constructs.

I could not disagree more strongly.

Swift takes the position that assertions are good and that it's better to crash in an unexpected state than to limp on. Force unwrapping is an example of that philosophy. A force unwrap is a "this is not nil" assertion.


(Robert Widmann) #16

The point is there *are* graceful ways of handling this that aren't unwrapping. See all the language features I mentioned in the original. There is really very little excuse to use a dereference operator all the same without a guard, precondition, or other static guarantee, and the language has given you the tools, many times over, to write those kinds of sanity checks.

Which is why I don't follow the logic for keeping ! around. In C and Objective-C, it is _never_ safe to dereference memory, even in cases where you have reasonable certainty about its validity. It seems to me making it more explicit where that logic gap is covered by intuition leads to higher quality code.

~Robert Widmann

2016/02/29 12:08、David Owens II <david@owensd.io> のメッセージ:

···

On Feb 29, 2016, at 6:54 AM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

I have to clarify here: I am not taking about .!, as!, as?, ?, etc. I am taking about making naked bang-unwrapping more difficult. That is, those cases when you write

let thing = optVal!

These are equivalent to naked unguarded dereferences in C-like languages and are, we can all agree, a code smell, not a feature. One would not ever recommend to a user of such languages that * or -> be used before checking that an object or region of memory was non-NULL. So, too, should we discourage this case by removing postfix-! from the language itself (notice it isn't even an operator, it is actually a part of the language).

I don't agree with this assertion. There are _many_ cases where you don't check for the pointer being null before accessing it. Those are programmer errors when the data is null. If there is no graceful way for the program to handle that case, what's the point of silently failing? Maybe later you come along because something keeps coming up with a null problem that you can't track down so you build something around it to recover.

I also don't see `let thing = unwrap(optVal)` or `let thing = optVal.unwrap()` as significantly better. The functionality still needs to be there, and this version is only slightly easier to grep vs. !.

-David


(Patrick Gili) #17

I agree with Brent's position on this. Simply put, there are use cases that forced unwrapping is necessary.

-Patrick

···

On Feb 29, 2016, at 3:40 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

Because of the above, I'd like to start a discussion about its deprecation and eventual removal in favor of either an unsafeUnwrap function or other extra-syntactic constructs. I don't intend for force-unwrapping as a concept to go away, as there will always be corner cases where it is necessary, but I don't believe it deserves to be so easy to do in a language that holds safety as a core tenant.

I could not disagree more strongly.

Swift takes the position that assertions are good and that it's better to crash in an unexpected state than to limp on. Force unwrapping is an example of that philosophy. A force unwrap is a "this is not nil" assertion.

That assertion often is not appropriate—namely, in code that tests for `nil`—and so we provide, by my count, five general ways to test-and-use nils (pattern matching, optional binding, optional chaining, defaulting, and mapping) without force unwrapping. These features are all great and, with *very* few exceptions, you should always use one of them rather than test-and-force-unwrap.

But where it *is* appropriate to assert non-nil, *we want you to force unwrap*. If, as you write your code, you are certain a value cannot be nil, we want your code to say so. That way, the code will crash if you're wrong, at the exact point where you made the mistake, where the problem will be easiest to diagnose. We do *not* want you to use a test-and-use construct in a place where you believe the test will always succeed, because you will probably not give any particular attention to the test-failed behavior, so it will probably be wrong.

In other words, making force unwrapping more difficult will not make people correctly handle unexpected nils—it will make them write code that *undetectably* does the wrong thing by skipping code that wasn't actually meant to be skipped. You'll see fewer crashes but more bugs. That runs counter to Swift's formulation of safety, and I think it would be a serious mistake.

I recently had the pleasure of debugging some force unwrap crashes in a view controller I hadn't touched in a while. The force unwraps I used very quickly demonstrated to me that my ideas about the code's lifecycle were wrong: for instance, I thought it would never be called before a certain property was set, but it turned out it sometimes was. If I had used a conditional construct without thinking through the consequences, that would have manifested as a mysteriously empty menu which I would have had trouble debugging. By stating explicitly in my code that I believed those values could not be nil, Swift quickly disproved my belief, showed me exactly what was wrong, and prompted me to correct it. That's the best thing it could have done for me in this situation.

I often see Swift style guides suggest that force-unwrapping is a strong code smell and should always be avoided, but I think that's wrongheaded. Test-and-force-unwrap *is* a code smell, and force-unwrap-when-you're-not-really-sure is incorrect too. But when you *are* sure, force unwrap is the very best way to write your code, because it will make any mistakes immediately obvious.

That's a feature, not a bug.

--
Brent Royal-Gordon
Architechies

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


(Brent Royal-Gordon) #18

These are equivalent to naked unguarded dereferences in C-like languages and are, we can all agree, a code smell, not a feature.

I previously wrote a 463-word post in this thread arguing that these are *not* code smells. Maybe you don't agree with that position, but if you really think "we can all agree" it's a code smell, you're not actually listening to the arguments against you.

I also object to the idea that these are "unguarded" dereferences. `!` *is* guarded by code which always reliably causes a crash if the value is nil. If this unwrap is guarded:

  guard let foo = foo else {
    fatalError("foo is nil!")
  }
  doSomething(foo)

Then so is this:

  doSomething(foo!)

···

--
Brent Royal-Gordon
Architechies


(marc hoffman) #19

This!

···

On Feb 29, 2016, at 4:40 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

Because of the above, I'd like to start a discussion about its deprecation and eventual removal in favor of either an unsafeUnwrap function or other extra-syntactic constructs. I don't intend for force-unwrapping as a concept to go away, as there will always be corner cases where it is necessary, but I don't believe it deserves to be so easy to do in a language that holds safety as a core tenant.

I could not disagree more strongly.

Swift takes the position that assertions are good and that it's better to crash in an unexpected state than to limp on. Force unwrapping is an example of that philosophy. A force unwrap is a "this is not nil" assertion.

That assertion often is not appropriate—namely, in code that tests for `nil`—and so we provide, by my count, five general ways to test-and-use nils (pattern matching, optional binding, optional chaining, defaulting, and mapping) without force unwrapping. These features are all great and, with *very* few exceptions, you should always use one of them rather than test-and-force-unwrap.

But where it *is* appropriate to assert non-nil, *we want you to force unwrap*. If, as you write your code, you are certain a value cannot be nil, we want your code to say so. That way, the code will crash if you're wrong, at the exact point where you made the mistake, where the problem will be easiest to diagnose. We do *not* want you to use a test-and-use construct in a place where you believe the test will always succeed, because you will probably not give any particular attention to the test-failed behavior, so it will probably be wrong.

In other words, making force unwrapping more difficult will not make people correctly handle unexpected nils—it will make them write code that *undetectably* does the wrong thing by skipping code that wasn't actually meant to be skipped. You'll see fewer crashes but more bugs. That runs counter to Swift's formulation of safety, and I think it would be a serious mistake.

I recently had the pleasure of debugging some force unwrap crashes in a view controller I hadn't touched in a while. The force unwraps I used very quickly demonstrated to me that my ideas about the code's lifecycle were wrong: for instance, I thought it would never be called before a certain property was set, but it turned out it sometimes was. If I had used a conditional construct without thinking through the consequences, that would have manifested as a mysteriously empty menu which I would have had trouble debugging. By stating explicitly in my code that I believed those values could not be nil, Swift quickly disproved my belief, showed me exactly what was wrong, and prompted me to correct it. That's the best thing it could have done for me in this situation.

I often see Swift style guides suggest that force-unwrapping is a strong code smell and should always be avoided, but I think that's wrongheaded. Test-and-force-unwrap *is* a code smell, and force-unwrap-when-you're-not-really-sure is incorrect too. But when you *are* sure, force unwrap is the very best way to write your code, because it will make any mistakes immediately obvious.

That's a feature, not a bug.

--
Brent Royal-Gordon
Architechies

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


(Charles Srstka) #20

Not that I disagree that force-unwrapping is sometimes necessary (especially when working with Cocoa idioms that pretty much require it, such as NSDocument and anything involving NIB outlets), but for this particular example, you can just use flatMap to do this:

let theResult = myArrayOfOptions.flatMap { $0 }.map { doSomethingTo($0) }

You could even combine the two into one flatMap with a nil test in it, although it ends up looking pretty ugly (but should perform better).

Charles

···

On Feb 29, 2016, at 2:33 AM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

There are some cases where it’s still very useful even in pure Swift code. For example, say I have an array of optional values, and want to trip nils, then perform a transform on the result, I would do something like:

  let theResult = myArrayOfOptions.filter({ $0 != nil }).map({ doSomethingTo($0!) })