> On Jun 29, 2017, at 1:05 AM, David Hart via swift-evolution > <swift-evolution@swift.org> wrote: > > 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 > 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 >> >> <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 <del...@apple.com >>> <mailto:del...@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 >>>>>> <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 >>>>> <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 >>>>> <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 >>>> <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 >>> <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
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution