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

Sorry, I fail to see how either wording enhances the expression of why
this operation is taking place. The entire explanation seems self-evident
to me in `!`.

It may not be though. How many times have you seen a force unwrap and
immediately thought the programmer to just be lazy, or new to Swift?

Never. (Now ask me what I immediately think of programmers who assert that
force unwrapping is a code smell...)

The !! syntax proposal at least makes these forces clearly an intentional

and thought out endeavor.

As I wrote, if that's the objection, then we might as well simply coin a
new operator spelled `!!!`, identical in behavior to `!` but called the
"intentional and thought-out force-unwrapping operator." Equally, it'd be
an argument to retire postfix `!` and to introduce an infix `!` which
mandates an explanation.

This is actually, on rethink, a diametrically opposite argument to the
first one posed by Ben Cohen. That argument claims that there's sometimes a
need to explain the context of a force-unwrap with a human-readable
rationale; just like there's room for a one-argument precondition and a
two-argument precondition, there should be room for a two-argument
force-unwrap. Here, there's a claim that people assume all force-unwrapping
to be poorly thought out without a human-readable rationale. This is,
again, really an argument that there is no good unannotated force-unwrap.

I reject both these notions. Having seen the examples given above, I'm now
leaning towards the conclusion that there is nothing in the way of
explanation in a string that can usefully elaborate upon the very
unambiguous statement that is a force unwrap.

¡¡¡

On Wed, Jun 28, 2017 at 9:50 PM, Alan Westbrook <alan@rockwoodsoftware.com> wrote:

On Jun 28, 2017, at 6:38 PM, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

At the risk of belaboring the point, allow me to develop this intuition
further. The second argument in a precondition is classically phrased "X
must be Y" or "X is not Y," and it serves two purposes (as I understand
it). If the precondition fails, then the reader understands that it is
because X is not Y. If the precondition succeeds, the reader is assured
that the precondition has tested that X is in fact Y.

Two uses have been advanced for the RHS of the proposed operator. The first
is, in the case of failure, to explain the failure without requiring
surrounding code context. The second is to explain to the reader, in the
context of the code, why success should be assured. But, this does not
imply that the underlying reason why success is assured is in turn also
true if the force unwrapping operation succeeds.

_The only error message_ that satisfies both of the purposes of a
precondition message in the case of a force unwrap is "X is not nil." In
your example, "params must be URL-escaped" cannot fulfill both of these
uses, because successful unwrapping is not preconditioned on params being
in fact URL-escaped. OTOH, "params must be ASCII-encoded" is not necessary
as a message because it is implied in the meaning of `!`.

¡¡¡

On Wed, Jun 28, 2017 at 9:50 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Jun 28, 2017 at 8:54 PM, Paul Cantrell <paul@bustoutsolutions.com> > wrote:

On Jun 28, 2017, at 8:32 PM, Xiaodi Wu via swift-evolution < >> swift-evolution@swift.org> wrote:

I would like to see an example where this string plausibly makes the
difference between having to hunt down the code and not have to do so. I do
not believe that "array must not be empty" or "array guaranteed non-empty"
is such an example, and I cannot myself imagine another scenario where it
would make such a difference.

You needn’t imagine. There was one up-thread:

  let paramData = params.data(using: String.Encoding.ascii)!

Huh? Why is force unwrap safe here? OK, the code plainly says the author
thinks that `params` must already be ASCII, but why is that a safe
assumption? What reasoning lead to that? What other sections of the code
does that reasoning depend on? If we get a crash on this line of code, what
chain of assumptions should we follow to discover the change that broke the
original author’s reasoning behind the force unwrap?

This is a job for a comment:

  let paramData = params.data(using: String.Encoding.ascii)! // params
is URL-escaped, thus already ASCII

Aha, it’s URL escaped.

That comment does not repeat information already stated in the code
itself. It does what any good comment does: it explains intent, context,
and rationale. It doesn’t restate _what_, but rather explains _why_.

For those who appreciate comments like that, this proposal simply allows
them to surface at runtime:

  let paramData = params.data(using: String.Encoding.ascii) !! "params
is URL-escaped, thus already ASCII"

And those who see no value in such a runtime message — and thus likely
also see no value such a comment — are free not to use either.

If this is the most convincing example, then I'd actually be adamantly
_against_ such an operator (where now I'm merely skeptical and would like
to see evidence of usefulness). This example is, quite simply, _wrong_.
Here's why:

First, if force unwrapping fails, the message should explain why it
failed: the reason why it failed is _not_ because it's URL-escaped and
_not_ because it's ASCII, but rather because it's **not** ASCII.

Second, even supposing the wording were fixed, it's at best not more
useful than `!` and at worst misleading. If the error message is "params
not ASCII-encoded" then it restates the code itself. If the error message
is "params not URL-escaped," then it's misleading, as that's not at all
what the LHS is actually asserting: it can be unwrapped *whether or not*
it's URL-escaped and it only matters that it's ASCII. You **absolutely
cannot** proceed from this point in the code assuming that `paramData` is a
URL-escaped string.

I would like to see an example where this string plausibly makes the difference between having to hunt down the code and not have to do so. I do not believe that "array must not be empty" or "array guaranteed non-empty" is such an example, and I cannot myself imagine another scenario where it would make such a difference.

You needn’t imagine. There was one up-thread:

  let paramData = params.data(using: String.Encoding.ascii)!

Huh? Why is force unwrap safe here? OK, the code plainly says the author thinks that `params` must already be ASCII, but why is that a safe assumption? What reasoning lead to that? What other sections of the code does that reasoning depend on? If we get a crash on this line of code, what chain of assumptions should we follow to discover the change that broke the original author’s reasoning behind the force unwrap?

This is a job for a comment:

  let paramData = params.data(using: String.Encoding.ascii)! // params is URL-escaped, thus already ASCII

Aha, it’s URL escaped.

That comment does not repeat information already stated in the code itself. It does what any good comment does: it explains intent, context, and rationale. It doesn’t restate _what_, but rather explains _why_.

For those who appreciate comments like that, this proposal simply allows them to surface at runtime:

  let paramData = params.data(using: String.Encoding.ascii) !! "params is URL-escaped, thus already ASCII"

And those who see no value in such a runtime message — and thus likely also see no value such a comment — are free not to use either.

If this is the most convincing example, then I'd actually be adamantly _against_ such an operator (where now I'm merely skeptical and would like to see evidence of usefulness). This example is, quite simply, _wrong_. Here's why:

First, if force unwrapping fails, the message should explain why it failed: the reason why it failed is _not_ because it's URL-escaped and _not_ because it's ASCII, but rather because it's **not** ASCII.

Fine, then:

    let paramData = params.data(using: String.Encoding.ascii) !! “params must be URL-escaped, and thus ASCII"

…or format the runtime message to fit that style of phrasing:

    fatal error: unexpectedly found nil while unwrapping an Optional value
    Failing underlying assumption:
        params is URL-escaped, thus already ASCII
    Current stack trace:
        â€Ś

Second, even supposing the wording were fixed, it's at best not more useful than `!` and at worst misleading.

…

If the error message is "params not URL-escaped," then it's misleading, as that's not at all what the LHS is actually asserting: it can be unwrapped *whether or not* it's URL-escaped and it only matters that it's ASCII.

Yes, of _course_ it’s not what the LHS is actually asserting. That is precisely the point of having a message. There is pertinent information not already present in the code.

The message describes an invariant not captured by the type system. In other words, the author of this code believes they have guaranteed something that the compiler itself cannot check. Thus this statement is exactly backwards:

You **absolutely cannot** proceed from this point in the code assuming that `paramData` is a URL-escaped string.

The author of this code is telling you they have _already_ proceeded from this point assuming that `paramData` is URL-escaped. They may have assumed wrong, but even if they did, this insight into the developer’s thinking is valuable.

Indeed, though we try to minimize it, there will inevitably be code that relies on assumptions the compiler cannot check. That is why the language has ! in the first place. Those uncheckable assumptions are not always self-evident. This is why languages provide comments and diagnostic messages.

Literalism nit-picking over the semantics of the RHS deliberately miss the original argument: the RHS is information-bearing.

Cheers, P

¡¡¡

On Jun 28, 2017, at 9:50 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Wed, Jun 28, 2017 at 8:54 PM, Paul Cantrell <paul@bustoutsolutions.com <mailto:paul@bustoutsolutions.com>> wrote:

On Jun 28, 2017, at 8:32 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Yeah, well I’m not a fan of the ‘syntactically convenience features only’ like `!!` as well, but I wouldn’t mind having the `??` overload now.

¡¡¡

--
Adrian Zubarev
Sent with Airmail

Am 28. Juni 2017 um 23:57:59, Jaden Geller (jaden.geller@gmail.com) schrieb:

I realize that, which is why I’m strongly against this proposal. I could potentially be for adding the `??` overload temporarily, but I don’t really feel it is necessary either.

On Jun 28, 2017, at 2:55 PM, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:

Besides all that Never as a bottom type means that you can use fatalError() literally everywhere not only on the RHS of ??.

func foo(_: Int) {}

foo(fatalError())
That said, we can kill our application everywhere we would ever imagine, regardless of whether there is ? or ! operator present somewhere.

--
Adrian Zubarev
Sent with Airmail

Am 28. Juni 2017 um 23:50:18, Jaden Geller via swift-evolution (swift-evolution@swift.org) schrieb:

I’m strongly against not using the `??` operator for `x ?? fatalError()` since that is naturally what will be possible once the `Never` type is a real bottom type. If you want to use `!!` for the `x !! “bad things!”` convenience form, I don’t care. But the `Never` form should definitely, definitely use `??`.

On Jun 28, 2017, at 1:30 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

Based on the feedback on this thread, I'm coming to the following conclusions:

`!!` sends the right semantic message. "Unwrap or die" is an unsafe operation. It is based on `!`, the unsafe forced unwrap operator, and not on `??`, the safe fallback nil-coalescing operator. Its symbology should therefore follow `!` and not `?`.

The `!!` operator should follow the same semantics as `Optional.unsafelyUnwrapped`, which establishes a precedent for this approach:

"The unsafelyUnwrapped property provides the same value as the forced unwrap operator (postfix !). However, in optimized builds (-O), no check is performed to ensure that the current instance actually has a value. Accessing this property in the case of a nil value is a serious programming error and could lead to undefined behavior or a runtime error."

By following `Optional.unsafelyUnwrapped`, this approach is consistent with https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst#logic-failures

"Logic failures are intended to be handled by fixing the code. It means checks of logic failures can be removed if the code is tested enough.

Actually checks of logic failures for various operations, `!`, `array[i]`, `&+` and so on, are designed and implemented to be removed
when we use `-Ounchecked`. It is useful for heavy computation like image processing and machine learning in which overhead of those checks is not permissible."

The right hand side should use a string (or more properly a string autoclosure) in preference to using a `Never` bottom type or a `() -> Never` closure. A string provides the cleanest user experience, and allows the greatest degree of self-documentation.

- A string is cleaner and more readable to type. It respects DRY, and avoids using *both* the operator and the call to `fatalError` or `preconditionFailure` to signal an unsafe condition:
`let last = array.last !! “Array guaranteed non-empty" // readable`
than:
`let last = array.last !! fatalError(“Array guaranteed non-empty”) // redundant, violates DRY`

- A string allows the operator *itself* to unsafely fail, just as the unary version of `!` does now. It does this with additional feedback to the developer during testing, code reading, and code maintenance. The string provides a self-auditing in-line annotation of the reason why the forced unwrap has been well considered, using a language construct to support this.

- A string disallows a potentially unsafe `Never` call that does not reflect a serious programming error, for example:
let last = array.last !! f() // where func f() -> Never { while true {} }

- Although as several list members mention, a `Never` closure solution is available today in Swift, so is the `!!` operator solution. Neither one requires a fundamental change to the language.

- Pushing forward on this proposal does not in any way reflect on adopting the still-desirable `Never` bottom type.

On Jun 28, 2017, at 12:42 PM, Tony Allevato via swift-evolution <swift-evolution@swift.org> wrote:

On Wed, Jun 28, 2017 at 11:15 AM Dave DeLong <delong@apple.com> wrote:
On Jun 28, 2017, at 10:44 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Well the main debate is that, we all want early access to a feature that will be part of Swift as soon as `Never` becomes the bottom type. When this happens the `??` will automatically support the pitched behavior. Until then if we all agree that we should add it now in a way that will not break anything we can simply add an overload to `??` as I previously showed.

I believe we should add it now, but I like the recent observation that making ?? suddenly become a potentially crashing operator violates the expectation that ? is an indication of safety.

?? does *not* become a potentially crashing operator. The *fatalError* (or whatever else the user chooses to put there) on the right-hand side is the crashing operation.

On the other hand, the existing semantics of Swift are that ! is always dangerous, so making !! be the a potentially crashing operator is much more consistent with the language.

There is no need for `!!` because it will fade in the future. If you think of `Never` as a bottom type now then `??` will already make total sense. The default value for T from rhs might be T or Never.

I respectfully disagree with your absolute position on this topic. Even with Never as a bottom type in the future, it would still be more convenient for me to type:

let last = array.last !! “Array must be non-empty"

… than it ever would be to type:

let last = array.last ?? fatalError(“Array must be non-empty”)

There is a very high bar for additions to the standard library—a new operator added to the language is going to be around (1) forever, or (2) indefinitely with some migration cost to users if it's ever removed. Shaving off a few keystrokes doesn't quite meet that bar—especially when an alternative has been shown to work already that provides the same functionality, is more general (not coupled to fatalError or String messages), and that fits better into Swift's design.

To make sure I'm not being too much of a downer, I would completely support this broader feature being implemented by that alternative: the ?? + autoclosure () -> Never combo. Then once Never does become a true bottom type, I believe it could be removed and the calling code would still *just work*.

Dave

@erica: the rhs argument should be called something like `noreturnOrError` and not `defaultValue`. And we should keep in mind that when Never becomes the bottom type we have to remove that overload from stdlib, because otherwise it will be ambiguous.

---

On the other hand if we tackle a different operator then we should rething the 'default value operator' because the second ? signals an optional but not a non-optional or an inplicit unwrapped operator. In that case I personally thing ?! would make more sense. Unwrap or (non-optional | IUO | trap/die)

--
Adrian Zubarev
Sent with Airmail
Am 28. Juni 2017 um 18:13:18, Tony Allevato via swift-evolution (swift-evolution@swift.org) schrieb:

It's hard for me to articulate, but "foo !! message" feels a little too much like a Perl-ism for my taste. Objectively that's not a great criticism on its own, but I just don't like the "smell" of an operator that takes a value on one side and a string for error reporting purposes on the other. It doesn't feel like it fits the style of Swift. I prefer a version that makes the call to fatalError (and thus, any other non-returning handler) explicitly written out in code.

So, if the language can already support this with ?? and autoclosure/Never as was shown above, I'd rather see that added to the language instead of a new operator that does the same thing (and is actually less general).

On Wed, Jun 28, 2017 at 8:52 AM Jacob Williams via swift-evolution <swift-evolution@swift.org> wrote:
I feel that the !! operator would be necessary for indicating that if this fails then something went horribly wrong somewhere and we should throw the fatalError. This allows the inclusion of optimizations using -Ounchecked and is clear that this is an operation that could result in a runtime error just like force unwrapping.

If we want code clarity and uniformity, then I think !! Is much better than ?? because it goes right along with the single ! Used for force unwrapping. However, this does depend on if the operator would be returning some kind of error that would cause the program to exit.

I think the ?? operator should not cause a program to exit early. It goes against optional unwrapping principles. I think code could get very confusing if some ? would return nil/a default value, and others would be causing your program to crash and exit. The ? operators should always be classified as safe operations.

On Jun 28, 2017, at 9:41 AM, Ben Cohen via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 28, 2017, at 8:27 AM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Count me in as a strong proponent of ?? () -> Never. We don't need to burden the language with an extra operator just for that.

You could say the same about ??

The concern that an additional operator (and one that, IMO, fits well into existing patterns) is so burdensome seems way overweighted in this discussion IMO.

Adding the operator, and encouraging its use, will help foster better understanding of optionals and legitimate use of force-unwrapping in a way that I don’t think `?? fatalError` could.

_______________________________________________
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
_______________________________________________
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
_______________________________________________
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

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

I’ve taken time to digest all the messages on this discussion and would like to summarise my point of view concerning several topics:

Usefulness of messages

Xiaodi seems to question the usefulness of attaching more information to the failure case of an optional's unwrapping. To his credit, the original example ("Array guaranteed non-empty") don’t add much. Instead, I think we should see those strings as a great opportunity to add application-specific business logic context that help debugging when the unwrapping fails. For example, let’s imagine that I am handling the log out operation for a user I know exists, I could write this:

let user = database.users[userId] !! “User to logout does not exist”

To expand on the usefulness of these messages, here’re some scattered examples of how I’ve been using this operator in an app I work on, pulled from various parts of the code:

// in a right-click gesture recognizer action handler
let event = NSApp.currentEvent !! "Trying to get current event for right click, but there's no event”

// 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"

// providing a value based on an initializer that returns an optional:
lazy var emptyURL: URL = { return URL(string: “myapp://section/\(identifier)") !! "can't create basic empty url” }()

// retrieving an image from an embedded framework:
    private static let addImage: NSImage = {
        let bundle = Bundle(for: FlagViewController.self)
        let image = bundle.image(forResource: "add") !! "Missing 'add' image"
        image.isTemplate = true
        return image
    }()

// asserting consistency of an internal model
let flag = command.flag(with: flagID) !! "Unable to retrieve non-custom flag for id \(flagID.string)"

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

1. Asserting system framework correctness

  For example, the “NSApp.currentEvent” property returns an Optional<NSEvent>, because there’s not always a current event going on. That’s fine. But when I’m in the action handler of a right-click gesture recognizer it is safe to assert that I do have an event. If this ever fails, I have an immediately clear description of where the system framework has not worked according to my expectations.

2. Asserting app logic correctness

  For example, I use this to assert that my outlets are properly hooked up (and the message tells me explicitly which outlet I’ve forgotten), or that my internal data model is in a consistent state.

Both areas of usage have been extremely helpful in building my app. They help me identify when I forget to put resources in the right target, or when I make changes to the internal model but forget all the places I’m supposed to insert things. They help me catch when I fat-finger a URL.

Yes, I could absolutely have done all of this with just a bare unwrap operator, but by putting the diagnostic message in there, I get immediate feedback as to why my code is failing. I don’t have to go digging around in the code in order to re-teach myself of what invariants are supposed to be held, because the error message gives me the succinct and immediately-actionable thing to do.

Never and new operator

If we introduce the new operator !! solely with the String override, I still have some doubts about it pulling its own weight. Of course, we could add a () -> Never override to increase its usefulness:

let user = database.users[userId] !! “User to logout does not exist”
let user = database.users[userId] !! logFatalError(“User to logout does not exist”)

As I demonstrate above even just the string version can be extremely helpful.

But Jaden Geller makes a very good point: if and once Never becomes a true bottom type, that syntax will be redundant because Never will be usable with the ?? operator, creating a lot of confusion:

let user = database.users[userId] !! logFatalError(“User to logout does not exist”)
let user = database.users[userId] ?? logFatalError(“User to logout does not exist”)

Maybe the answer then is to add !! for strings, and then use ?? If you have a custom Never function.

Those two lines will have exactly the same effect.

Cognitive Dissonance of Never and ??

Ben Cohen originally mentioned that if we introduce a () -> Never overload of the ?? operator, there will be cognitive dissonance because the question mark operator is never used in Swift to signal a trap. But if we make Never a true bottom type, which has a lot of advantages, this dissonance will be unavoidable. Shouldn’t we embrace it then?

Potential for confusion with multiple per line

This might not be a real concern, but are we introducing a syntax that will make it possible/encourage people to write less than readable code?

let user = (database !! “Database is not operational”).users[userId !! “User was not set in time”] !! “User to logout does not exist"

IMO, this is a bit of a red herring, because you can already write similarly atrocious code in Swift today. The addition of “!!” wouldn’t really change that.

Cheers,

Dave

¡¡¡

On Jun 29, 2017, at 1:05 AM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

David.

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

Based on the feedback on this thread, I'm coming to the following conclusions:

`!!` sends the right semantic message. "Unwrap or die" is an unsafe operation. It is based on `!`, the unsafe forced unwrap operator, and not on `??`, the safe fallback nil-coalescing operator. Its symbology should therefore follow `!` and not `?`.

The `!!` operator should follow the same semantics as `Optional.unsafelyUnwrapped`, which establishes a precedent for this approach:

> "The unsafelyUnwrapped property provides the same value as the forced unwrap operator (postfix !). However, in optimized builds (-O), no check is performed to ensure that the current instance actually has a value. Accessing this property in the case of a nil value is a serious programming error and could lead to undefined behavior or a runtime error."

By following `Optional.unsafelyUnwrapped`, this approach is consistent with https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst#logic-failures

> "Logic failures are intended to be handled by fixing the code. It means checks of logic failures can be removed if the code is tested enough.
Actually checks of logic failures for various operations, `!`, `array[i]`, `&+` and so on, are designed and implemented to be removed
when we use `-Ounchecked`. It is useful for heavy computation like image processing and machine learning in which overhead of those checks is not permissible."

The right hand side should use a string (or more properly a string autoclosure) in preference to using a `Never` bottom type or a `() -> Never` closure. A string provides the cleanest user experience, and allows the greatest degree of self-documentation.

- A string is cleaner and more readable to type. It respects DRY, and avoids using *both* the operator and the call to `fatalError` or `preconditionFailure` to signal an unsafe condition:
`let last = array.last !! “Array guaranteed non-empty" // readable`
than:
`let last = array.last !! fatalError(“Array guaranteed non-empty”) // redundant, violates DRY`

- A string allows the operator *itself* to unsafely fail, just as the unary version of `!` does now. It does this with additional feedback to the developer during testing, code reading, and code maintenance. The string provides a self-auditing in-line annotation of the reason why the forced unwrap has been well considered, using a language construct to support this.

- A string disallows a potentially unsafe `Never` call that does not reflect a serious programming error, for example:
let last = array.last !! f() // where func f() -> Never { while true {} }

- Although as several list members mention, a `Never` closure solution is available today in Swift, so is the `!!` operator solution. Neither one requires a fundamental change to the language.

- Pushing forward on this proposal does not in any way reflect on adopting the still-desirable `Never` bottom type.

On Jun 28, 2017, at 12:42 PM, Tony Allevato via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Wed, Jun 28, 2017 at 11:15 AM Dave DeLong <delong@apple.com <mailto:delong@apple.com>> wrote:

On Jun 28, 2017, at 10:44 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Well the main debate is that, we all want early access to a feature that will be part of Swift as soon as `Never` becomes the bottom type. When this happens the `??` will automatically support the pitched behavior. Until then if we all agree that we should add it now in a way that will not break anything we can simply add an overload to `??` as I previously showed.

I believe we should add it now, but I like the recent observation that making ?? suddenly become a potentially crashing operator violates the expectation that ? is an indication of safety.

?? does *not* become a potentially crashing operator. The *fatalError* (or whatever else the user chooses to put there) on the right-hand side is the crashing operation.

On the other hand, the existing semantics of Swift are that ! is always dangerous, so making !! be the a potentially crashing operator is much more consistent with the language.

There is no need for `!!` because it will fade in the future. If you think of `Never` as a bottom type now then `??` will already make total sense. The default value for T from rhs might be T or Never.

I respectfully disagree with your absolute position on this topic. Even with Never as a bottom type in the future, it would still be more convenient for me to type:

let last = array.last !! “Array must be non-empty"

… than it ever would be to type:

let last = array.last ?? fatalError(“Array must be non-empty”)

There is a very high bar for additions to the standard library—a new operator added to the language is going to be around (1) forever, or (2) indefinitely with some migration cost to users if it's ever removed. Shaving off a few keystrokes doesn't quite meet that bar—especially when an alternative has been shown to work already that provides the same functionality, is more general (not coupled to fatalError or String messages), and that fits better into Swift's design.

To make sure I'm not being too much of a downer, I would completely support this broader feature being implemented by that alternative: the ?? + autoclosure () -> Never combo. Then once Never does become a true bottom type, I believe it could be removed and the calling code would still *just work*.

Dave

@erica: the rhs argument should be called something like `noreturnOrError` and not `defaultValue`. And we should keep in mind that when Never becomes the bottom type we have to remove that overload from stdlib, because otherwise it will be ambiguous.

---

On the other hand if we tackle a different operator then we should rething the 'default value operator' because the second ? signals an optional but not a non-optional or an inplicit unwrapped operator. In that case I personally thing ?! would make more sense. Unwrap or (non-optional | IUO | trap/die)

--
Adrian Zubarev
Sent with Airmail
Am 28. Juni 2017 um 18:13:18, Tony Allevato via swift-evolution (swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:

It's hard for me to articulate, but "foo !! message" feels a little too much like a Perl-ism for my taste. Objectively that's not a great criticism on its own, but I just don't like the "smell" of an operator that takes a value on one side and a string for error reporting purposes on the other. It doesn't feel like it fits the style of Swift. I prefer a version that makes the call to fatalError (and thus, any other non-returning handler) explicitly written out in code.

So, if the language can already support this with ?? and autoclosure/Never as was shown above, I'd rather see that added to the language instead of a new operator that does the same thing (and is actually less general).

On Wed, Jun 28, 2017 at 8:52 AM Jacob Williams via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I feel that the !! operator would be necessary for indicating that if this fails then something went horribly wrong somewhere and we should throw the fatalError. This allows the inclusion of optimizations using -Ounchecked and is clear that this is an operation that could result in a runtime error just like force unwrapping.

If we want code clarity and uniformity, then I think !! Is much better than ?? because it goes right along with the single ! Used for force unwrapping. However, this does depend on if the operator would be returning some kind of error that would cause the program to exit.

I think the ?? operator should not cause a program to exit early. It goes against optional unwrapping principles. I think code could get very confusing if some ? would return nil/a default value, and others would be causing your program to crash and exit. The ? operators should always be classified as safe operations.

On Jun 28, 2017, at 9:41 AM, Ben Cohen via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jun 28, 2017, at 8:27 AM, David Hart via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Count me in as a strong proponent of ?? () -> Never. We don't need to burden the language with an extra operator just for that.

You could say the same about ??

The concern that an additional operator (and one that, IMO, fits well into existing patterns) is so burdensome seems way overweighted in this discussion IMO.

Adding the operator, and encouraging its use, will help foster better understanding of optionals and legitimate use of force-unwrapping in a way that I don’t think `?? fatalError` could.

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

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

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

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

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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

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.

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

¡¡¡

Il giorno 29 giu 2017, alle ore 03:18, Ben Cohen via swift-evolution <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.

Sure, but... a lot of unwrap-failed explanations are never going to be
printed because a lot of unwraps will never fail. We're going to spend time
writing those explanations, and we're going to increase the size of our
binaries. Maybe making the bug-screeners do more work is still a net win
for humanity.

¡¡¡

On Wed, Jun 28, 2017 at 8:49 PM, Ben Cohen via swift-evolution < swift-evolution@swift.org> wrote:

As the screener of a non-zero number of radars resulting from unwrapped
nils, I would certainly appreciate more use of guard let x = x else {
fatalError(“explanation”) } and hope that !! would encourage it.

In the initial example, repeated here in largely identical form, the desired comment is "array must be non-empty." In what way does that provide more information than a bare `!`?

By the same token, why does precondition have an optional string? Why would you ever want to write more than:

precondition(!array.isEmpty)

It’s self evident – the array must not be empty! Why the second argument?

Because you might want a meaningful string to be output when the app traps. And it gives you somewhere to document why it mustn’t be empty – the explanation for the precondition rather than the precondition itself. Similarly, when your app traps because of an unwrapped nil, it’s desirable to get some output in your debug console telling you why straight away, rather than have to hunt down the code, read the line that failed, then read the context around the line, to understand the reason. This is even more important if you didn’t write this code yourself.

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.

I’m so glad the above has been stated outright here. It’s a bugbear of mine hearing people preach that the ! operator is always wrong.

¡¡¡

On 29 Jun 2017, at 11:18 am, Ben Cohen via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 28, 2017, at 5:27 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

In cases where it’s really not necessary, ! would remain just like, when you’re not really too fussed, you can leave off the string from a precondition.

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

That the functionality exists to provide messages with fatal errors is an indication that they do have some utility.

Alan

¡¡¡

On Jun 28, 2017, at 8:02 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

I reject both these notions. Having seen the examples given above, I'm now leaning towards the conclusion that there is nothing in the way of explanation in a string that can usefully elaborate upon the very unambiguous statement that is a force unwrap.

Not at all. A message is required for `fatalError` simply to explain _what_
the error is. By contrast, it has already been conceded that there is
absolutely no confusion as to _what_ the error is in the case of a failed
force-unwrap. Rather, as Ben Cohen argued above, the idea behind this
proposal is that it is useful to explain the "why"--on the premise that
there is some "why" which is not deducible from the "what." My argument is
that there is no suitable "why" which can be suitably expressed in a
sugared form of a fatalError string argument.

¡¡¡

On Wed, Jun 28, 2017 at 10:18 PM, Alan Westbrook <alan@rockwoodsoftware.com> wrote:

On Jun 28, 2017, at 8:02 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

I reject both these notions. Having seen the examples given above, I'm now
leaning towards the conclusion that there is nothing in the way of
explanation in a string that can usefully elaborate upon the very
unambiguous statement that is a force unwrap.

That the functionality exists to provide messages with fatal errors is an
indication that they do have some utility.

I would like to see an example where this string plausibly makes the
difference between having to hunt down the code and not have to do so. I do
not believe that "array must not be empty" or "array guaranteed non-empty"
is such an example, and I cannot myself imagine another scenario where it
would make such a difference.

You needn’t imagine. There was one up-thread:

  let paramData = params.data(using: String.Encoding.ascii)!

Huh? Why is force unwrap safe here? OK, the code plainly says the author
thinks that `params` must already be ASCII, but why is that a safe
assumption? What reasoning lead to that? What other sections of the code
does that reasoning depend on? If we get a crash on this line of code, what
chain of assumptions should we follow to discover the change that broke the
original author’s reasoning behind the force unwrap?

This is a job for a comment:

  let paramData = params.data(using: String.Encoding.ascii)! // params
is URL-escaped, thus already ASCII

Aha, it’s URL escaped.

That comment does not repeat information already stated in the code
itself. It does what any good comment does: it explains intent, context,
and rationale. It doesn’t restate _what_, but rather explains _why_.

For those who appreciate comments like that, this proposal simply allows
them to surface at runtime:

  let paramData = params.data(using: String.Encoding.ascii) !! "params
is URL-escaped, thus already ASCII"

And those who see no value in such a runtime message — and thus likely
also see no value such a comment — are free not to use either.

If this is the most convincing example, then I'd actually be adamantly
_against_ such an operator (where now I'm merely skeptical and would like
to see evidence of usefulness). This example is, quite simply, _wrong_.
Here's why:

First, if force unwrapping fails, the message should explain why it
failed: the reason why it failed is _not_ because it's URL-escaped and
_not_ because it's ASCII, but rather because it's **not** ASCII.

Fine, then:

    let paramData = params.data(using: String.Encoding.ascii) !!
“params must be URL-escaped, and thus ASCII"

…or format the runtime message to fit that style of phrasing:

    fatal error: unexpectedly found nil while unwrapping an Optional value
    Failing underlying assumption:
        params is URL-escaped, thus already ASCII
    Current stack trace:
        â€Ś

Second, even supposing the wording were fixed, it's at best not more
useful than `!` and at worst misleading.

…

If the error message is "params not URL-escaped," then it's misleading, as
that's not at all what the LHS is actually asserting: it can be unwrapped
*whether or not* it's URL-escaped and it only matters that it's ASCII.

Yes, of _course_ it’s not what the LHS is actually asserting. That is
precisely the point of having a message. There is pertinent information not
already present in the code.

See below.

The message describes an invariant not captured by the type system. In
other words, the author of this code believes they have guaranteed
something that the compiler itself cannot check. Thus this statement is
exactly backwards:

You **absolutely cannot** proceed from this point in the code assuming
that `paramData` is a URL-escaped string.

The author of this code is telling you they have _already_ proceeded from
this point assuming that `paramData` is URL-escaped.

No, the author proceeded from a previous point assuming that `params` is
URL-escaped. The act of unwrapping does not demarcate the transition
between where `params` is assumed to be URL-escaped and where it is not.
Execution can proceed past this point even if `params` is *not* URL-escaped.

They may have assumed wrong, but even if they did, this insight into the

developer’s thinking is valuable.

Indeed, though we try to minimize it, there will inevitably be code that
relies on assumptions the compiler cannot check. That is why the language
has ! in the first place. Those uncheckable assumptions are not always
self-evident. This is why languages provide comments and diagnostic
messages.

Literalism nit-picking over the semantics of the RHS deliberately miss the
original argument: the RHS is information-bearing.

_Of course_ the goal is to nail down the semantics of the RHS. It's a
proposal for an infix operator, for which the semantics of the LHS are not
in dispute. What else is there to talk about?

¡¡¡

On Wed, Jun 28, 2017 at 10:33 PM, Paul Cantrell <cantrell@pobox.com> wrote:

On Jun 28, 2017, at 9:50 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Wed, Jun 28, 2017 at 8:54 PM, Paul Cantrell <paul@bustoutsolutions.com> > wrote:

On Jun 28, 2017, at 8:32 PM, Xiaodi Wu via swift-evolution < >> swift-evolution@swift.org> wrote:

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

-- E

¡¡¡

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

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

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.

¡¡¡

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

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

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
https://lists.swift.org/mailman/listinfo/swift-evolution

Well duh. But that’s not what the message conveys.

Read x !! y as “x! /* which is guaranteed to succeed because y */”.

P

¡¡¡

On Jun 28, 2017, at 10:49 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Jun 28, 2017 at 10:33 PM, Paul Cantrell <cantrell@pobox.com <mailto:cantrell@pobox.com>> wrote:

On Jun 28, 2017, at 9:50 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Wed, Jun 28, 2017 at 8:54 PM, Paul Cantrell <paul@bustoutsolutions.com <mailto:paul@bustoutsolutions.com>> wrote:

On Jun 28, 2017, at 8:32 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I would like to see an example where this string plausibly makes the difference between having to hunt down the code and not have to do so. I do not believe that "array must not be empty" or "array guaranteed non-empty" is such an example, and I cannot myself imagine another scenario where it would make such a difference.

You needn’t imagine. There was one up-thread:

  let paramData = params.data(using: String.Encoding.ascii)!

Huh? Why is force unwrap safe here? OK, the code plainly says the author thinks that `params` must already be ASCII, but why is that a safe assumption? What reasoning lead to that? What other sections of the code does that reasoning depend on? If we get a crash on this line of code, what chain of assumptions should we follow to discover the change that broke the original author’s reasoning behind the force unwrap?

This is a job for a comment:

  let paramData = params.data(using: String.Encoding.ascii)! // params is URL-escaped, thus already ASCII

Aha, it’s URL escaped.

That comment does not repeat information already stated in the code itself. It does what any good comment does: it explains intent, context, and rationale. It doesn’t restate _what_, but rather explains _why_.

For those who appreciate comments like that, this proposal simply allows them to surface at runtime:

  let paramData = params.data(using: String.Encoding.ascii) !! "params is URL-escaped, thus already ASCII"

And those who see no value in such a runtime message — and thus likely also see no value such a comment — are free not to use either.

If this is the most convincing example, then I'd actually be adamantly _against_ such an operator (where now I'm merely skeptical and would like to see evidence of usefulness). This example is, quite simply, _wrong_. Here's why:

First, if force unwrapping fails, the message should explain why it failed: the reason why it failed is _not_ because it's URL-escaped and _not_ because it's ASCII, but rather because it's **not** ASCII.

Fine, then:

    let paramData = params.data(using: String.Encoding.ascii) !! “params must be URL-escaped, and thus ASCII"

…or format the runtime message to fit that style of phrasing:

    fatal error: unexpectedly found nil while unwrapping an Optional value
    Failing underlying assumption:
        params is URL-escaped, thus already ASCII
    Current stack trace:
        â€Ś

Second, even supposing the wording were fixed, it's at best not more useful than `!` and at worst misleading.

…

If the error message is "params not URL-escaped," then it's misleading, as that's not at all what the LHS is actually asserting: it can be unwrapped *whether or not* it's URL-escaped and it only matters that it's ASCII.

Yes, of _course_ it’s not what the LHS is actually asserting. That is precisely the point of having a message. There is pertinent information not already present in the code.

See below.

The message describes an invariant not captured by the type system. In other words, the author of this code believes they have guaranteed something that the compiler itself cannot check. Thus this statement is exactly backwards:

You **absolutely cannot** proceed from this point in the code assuming that `paramData` is a URL-escaped string.

The author of this code is telling you they have _already_ proceeded from this point assuming that `paramData` is URL-escaped.

No, the author proceeded from a previous point assuming that `params` is URL-escaped. The act of unwrapping does not demarcate the transition between where `params` is assumed to be URL-escaped and where it is not.

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.

Elviro

¡¡¡

Il giorno 29 giu 2017, alle ore 12:12, David Hart <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

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:

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

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.

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: "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

¡¡¡

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

On Jun 29, 2017, at 9:13 AM, Dave DeLong <delong@apple.com <mailto:delong@apple.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> 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
https://lists.swift.org/mailman/listinfo/swift-evolution

That is plainly contrary to what the proposal says. It is to be sugar for a
string argument to fatalError, which would be read: "x, which fails iff y."

¡¡¡

On Wed, Jun 28, 2017 at 23:35 Paul Cantrell <cantrell@pobox.com> wrote:

On Jun 28, 2017, at 10:49 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Jun 28, 2017 at 10:33 PM, Paul Cantrell <cantrell@pobox.com> > wrote:

On Jun 28, 2017, at 9:50 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Wed, Jun 28, 2017 at 8:54 PM, Paul Cantrell <paul@bustoutsolutions.com >> > wrote:

On Jun 28, 2017, at 8:32 PM, Xiaodi Wu via swift-evolution < >>> swift-evolution@swift.org> wrote:

I would like to see an example where this string plausibly makes the
difference between having to hunt down the code and not have to do so. I do
not believe that "array must not be empty" or "array guaranteed non-empty"
is such an example, and I cannot myself imagine another scenario where it
would make such a difference.

You needn’t imagine. There was one up-thread:

  let paramData = params.data(using: String.Encoding.ascii)!

Huh? Why is force unwrap safe here? OK, the code plainly says the author
thinks that `params` must already be ASCII, but why is that a safe
assumption? What reasoning lead to that? What other sections of the code
does that reasoning depend on? If we get a crash on this line of code, what
chain of assumptions should we follow to discover the change that broke the
original author’s reasoning behind the force unwrap?

This is a job for a comment:

  let paramData = params.data(using: String.Encoding.ascii)! // params
is URL-escaped, thus already ASCII

Aha, it’s URL escaped.

That comment does not repeat information already stated in the code
itself. It does what any good comment does: it explains intent, context,
and rationale. It doesn’t restate _what_, but rather explains _why_.

For those who appreciate comments like that, this proposal simply allows
them to surface at runtime:

  let paramData = params.data(using: String.Encoding.ascii) !! "params
is URL-escaped, thus already ASCII"

And those who see no value in such a runtime message — and thus likely
also see no value such a comment — are free not to use either.

If this is the most convincing example, then I'd actually be adamantly
_against_ such an operator (where now I'm merely skeptical and would like
to see evidence of usefulness). This example is, quite simply, _wrong_.
Here's why:

First, if force unwrapping fails, the message should explain why it
failed: the reason why it failed is _not_ because it's URL-escaped and
_not_ because it's ASCII, but rather because it's **not** ASCII.

Fine, then:

    let paramData = params.data(using: String.Encoding.ascii) !!
“params must be URL-escaped, and thus ASCII"

…or format the runtime message to fit that style of phrasing:

    fatal error: unexpectedly found nil while unwrapping an Optional value
    Failing underlying assumption:
        params is URL-escaped, thus already ASCII
    Current stack trace:
        â€Ś

Second, even supposing the wording were fixed, it's at best not more
useful than `!` and at worst misleading.

…

If the error message is "params not URL-escaped," then it's misleading,
as that's not at all what the LHS is actually asserting: it can be
unwrapped *whether or not* it's URL-escaped and it only matters that it's
ASCII.

Yes, of _course_ it’s not what the LHS is actually asserting. That is
precisely the point of having a message. There is pertinent information not
already present in the code.

See below.

The message describes an invariant not captured by the type system. In
other words, the author of this code believes they have guaranteed
something that the compiler itself cannot check. Thus this statement is
exactly backwards:

You **absolutely cannot** proceed from this point in the code assuming
that `paramData` is a URL-escaped string.

The author of this code is telling you they have _already_ proceeded from
this point assuming that `paramData` is URL-escaped.

No, the author proceeded from a previous point assuming that `params` is
URL-escaped. The act of unwrapping does not demarcate the transition
between where `params` is assumed to be URL-escaped and where it is not.

Well duh. But that’s not what the message conveys.

Read x !! y as “x! /* which is guaranteed to succeed because y */”.

P

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).

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

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.

Elviro

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.

¡¡¡

On Thu, Jun 29, 2017 at 12:45 PM, Elviro Rocca via swift-evolution < swift-evolution@swift.org> wrote:

--
VĂ­ctor Pimentel