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

Using an operator to provide feedback on the context of a failed unwrap has become a commonly implemented approach in the Swift developer Community. What are your thoughts about adopting this widely-used operator into the standard library?

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

Details here: bangbang.md · GitHub

Thank you for your thoughtful feedback, -- E

Finally found a few minutes to read this thread.

I'm a big fan of the `Never`-based approach. (I was before, but I am more so now.) Here are the points I can see in its favor:

1. It is extremely clear about what's happening—`!!` is another random operator to learn, but `fatalError(_:)` or `preconditionFailure(_:)` are fairly self-explanatory, and `??` is something you might already be using.

2. It allows you to control the optimization behavior by using `fatalError`, `preconditionFailure`, or `assertionFailure` as desired.

3. If we later change `throw` from being a statement to being a `Never`-returning expression, you could use `throw` on the right-hand side of `??`.

Hadn't thought of that. That's really cool!

4. It supports other `Never`-returning operations, like `abort()` or `exit(_:)` or your custom `usage()` function, on the right side of `??`.

That's very important IMHO.

···

On 30 Jun 2017, at 07:23, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 27, 2017, at 10:16 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

5. It supports file-and-line error reporting without having to add any new features; `!!` could not do this because an operator can't have extra, defaulted parameters to carry the file and line.

6. It harmonizes with the eventual idea of making `Never` a universal bottom type, but we don't actually have to implement that today, because we can just overload `??` for now.

Against these advantages, the only one I can see for `!!` is that it is terse. Terseness is good, especially for a feature which is competing with the single-character postfix `!` operator, but I can't help but be drawn to the flexibility and power of `??` with a `Never` expression on the right-hand side.

--
Brent Royal-Gordon
Architechies

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

Using an operator to provide feedback on the context of a failed unwrap has become a commonly implemented approach in the Swift developer Community. What are your thoughts about adopting this widely-used operator into the standard library?

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

Details here: bangbang.md · GitHub

Thank you for your thoughtful feedback, -- E

Finally found a few minutes to read this thread.

Thanks for your thoughts Brent. I generally agree.

I'm a big fan of the `Never`-based approach. (I was before, but I am more so now.) Here are the points I can see in its favor:

1. It is extremely clear about what's happening—`!!` is another random operator to learn, but `fatalError(_:)` or `preconditionFailure(_:)` are fairly self-explanatory, and `??` is something you might already be using.

2. It allows you to control the optimization behavior by using `fatalError`, `preconditionFailure`, or `assertionFailure` as desired.

3. If we later change `throw` from being a statement to being a `Never`-returning expression, you could use `throw` on the right-hand side of `??`.

What do you have in mind here? I don't recall any discussion of `throw` return Never. It seems like a novel use of a bottom type that might preclude the possibility of ever having a Result type that seamlessly bridges to Swift's error handling.

···

Sent from my iPad

On Jun 30, 2017, at 12:24 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 27, 2017, at 10:16 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

4. It supports other `Never`-returning operations, like `abort()` or `exit(_:)` or your custom `usage()` function, on the right side of `??`.

5. It supports file-and-line error reporting without having to add any new features; `!!` could not do this because an operator can't have extra, defaulted parameters to carry the file and line.

6. It harmonizes with the eventual idea of making `Never` a universal bottom type, but we don't actually have to implement that today, because we can just overload `??` for now.

Against these advantages, the only one I can see for `!!` is that it is terse. Terseness is good, especially for a feature which is competing with the single-character postfix `!` operator, but I can't help but be drawn to the flexibility and power of `??` with a `Never` expression on the right-hand side.

--
Brent Royal-Gordon
Architechies

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

These are all excellent points. I also feel they sidestep the motivation of the proposal. Even if there were a bottom `Never` and you could use `?? fatalError()`, I still think the language would benefit from `!!`.

As the language is right now, you can write your own "or die" function using a `guard` statement, overloading `??`, or implementing `!!`. Being able to duplicate an explanatory fail path isn't the point. Offering a best-practices alternative to `!!` that is easy to use, obvious to understand, and simple to adopt is.

As for the `#line` and `#file` issue, that is my biggest concern but I believe that can be surmounted.

-- E

···

On Jun 29, 2017, at 11:23 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

On Jun 27, 2017, at 10:16 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Using an operator to provide feedback on the context of a failed unwrap has become a commonly implemented approach in the Swift developer Community. What are your thoughts about adopting this widely-used operator into the standard library?

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

Details here: bangbang.md · GitHub

Thank you for your thoughtful feedback, -- E

Finally found a few minutes to read this thread.

I'm a big fan of the `Never`-based approach. (I was before, but I am more so now.) Here are the points I can see in its favor:

1. It is extremely clear about what's happening—`!!` is another random operator to learn, but `fatalError(_:)` or `preconditionFailure(_:)` are fairly self-explanatory, and `??` is something you might already be using.

2. It allows you to control the optimization behavior by using `fatalError`, `preconditionFailure`, or `assertionFailure` as desired.

3. If we later change `throw` from being a statement to being a `Never`-returning expression, you could use `throw` on the right-hand side of `??`.

4. It supports other `Never`-returning operations, like `abort()` or `exit(_:)` or your custom `usage()` function, on the right side of `??`.

5. It supports file-and-line error reporting without having to add any new features; `!!` could not do this because an operator can't have extra, defaulted parameters to carry the file and line.

6. It harmonizes with the eventual idea of making `Never` a universal bottom type, but we don't actually have to implement that today, because we can just overload `??` for now.

Against these advantages, the only one I can see for `!!` is that it is terse. Terseness is good, especially for a feature which is competing with the single-character postfix `!` operator, but I can't help but be drawn to the flexibility and power of `??` with a `Never` expression on the right-hand side.

--
Brent Royal-Gordon
Architechies

+1 to everything.

If terseness is the only clear advantage of a new operator that is meant to
be used sparsely, it's clear to me that the ?? Never form is better for the
Swift community as a whole.

···

On Fri, Jun 30, 2017 at 7:24 AM, Brent Royal-Gordon via swift-evolution < swift-evolution@swift.org> wrote:

On Jun 27, 2017, at 10:16 AM, Erica Sadun via swift-evolution < > swift-evolution@swift.org> wrote:

Using an operator to provide feedback on the context of a failed unwrap
has become a commonly implemented approach in the Swift developer
Community. What are your thoughts about adopting this widely-used operator
into the standard library?

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

Details here: erica’s gists · GitHub
423e4b1c63b95c4c90338cdff4939a9b

Thank you for your thoughtful feedback, -- E

Finally found a few minutes to read this thread.

I'm a big fan of the `Never`-based approach. (I was before, but I am more
so now.) Here are the points I can see in its favor:

1. It is extremely clear about what's happening—`!!` is another random
operator to learn, but `fatalError(_:)` or `preconditionFailure(_:)` are
fairly self-explanatory, and `??` is something you might already be using.

2. It allows you to control the optimization behavior by using
`fatalError`, `preconditionFailure`, or `assertionFailure` as desired.

3. If we later change `throw` from being a statement to being a
`Never`-returning expression, you could use `throw` on the right-hand side
of `??`.

4. It supports other `Never`-returning operations, like `abort()` or
`exit(_:)` or your custom `usage()` function, on the right side of `??`.

5. It supports file-and-line error reporting without having to add any new
features; `!!` could not do this because an operator can't have extra,
defaulted parameters to carry the file and line.

6. It harmonizes with the eventual idea of making `Never` a universal
bottom type, but we don't actually have to implement that today, because we
can just overload `??` for now.

Against these advantages, the only one I can see for `!!` is that it is
terse. Terseness is good, especially for a feature which is competing with
the single-character postfix `!` operator, but I can't help but be drawn to
the flexibility and power of `??` with a `Never` expression on the
right-hand side.

--
Víctor Pimentel

+1 Well summarized.

The !! operator feels more like:

func !! <T>(optional: T?, errorMessage: String) -> T {
   return optional ?? fatalError(errorMessage)
}
Which is totally unnecessary.

···

--
Adrian Zubarev
Sent with Airmail

Am 30. Juni 2017 um 07:24:04, Brent Royal-Gordon via swift-evolution (swift-evolution@swift.org) schrieb:

On Jun 27, 2017, at 10:16 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

Using an operator to provide feedback on the context of a failed unwrap has become a commonly implemented approach in the Swift developer Community. What are your thoughts about adopting this widely-used operator into the standard library?

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

Details here: bangbang.md · GitHub

Thank you for your thoughtful feedback, -- E

Finally found a few minutes to read this thread.

I'm a big fan of the `Never`-based approach. (I was before, but I am more so now.) Here are the points I can see in its favor:

1. It is extremely clear about what's happening—`!!` is another random operator to learn, but `fatalError(_:)` or `preconditionFailure(_:)` are fairly self-explanatory, and `??` is something you might already be using.

2. It allows you to control the optimization behavior by using `fatalError`, `preconditionFailure`, or `assertionFailure` as desired.

3. If we later change `throw` from being a statement to being a `Never`-returning expression, you could use `throw` on the right-hand side of `??`.

4. It supports other `Never`-returning operations, like `abort()` or `exit(_:)` or your custom `usage()` function, on the right side of `??`.

5. It supports file-and-line error reporting without having to add any new features; `!!` could not do this because an operator can't have extra, defaulted parameters to carry the file and line.

6. It harmonizes with the eventual idea of making `Never` a universal bottom type, but we don't actually have to implement that today, because we can just overload `??` for now.

Against these advantages, the only one I can see for `!!` is that it is terse. Terseness is good, especially for a feature which is competing with the single-character postfix `!` operator, but I can't help but be drawn to the flexibility and power of `??` with a `Never` expression on the right-hand side.

--
Brent Royal-Gordon
Architechies

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

These are good points, Brent.

Using an operator to provide feedback on the context of a failed unwrap has become a commonly implemented approach in the Swift developer Community. What are your thoughts about adopting this widely-used operator into the standard library?

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

Details here: bangbang.md · GitHub

Thank you for your thoughtful feedback, -- E

Finally found a few minutes to read this thread.

I'm a big fan of the `Never`-based approach. (I was before, but I am more so now.) Here are the points I can see in its favor:

1. It is extremely clear about what's happening—`!!` is another random operator to learn, but `fatalError(_:)` or `preconditionFailure(_:)` are fairly self-explanatory, and `??` is something you might already be using.

!! is also clear about what’s happening, because it’s just like the existing ! operator.

2. It allows you to control the optimization behavior by using `fatalError`, `preconditionFailure`, or `assertionFailure` as desired.

Yep. That’s cool.

3. If we later change `throw` from being a statement to being a `Never`-returning expression, you could use `throw` on the right-hand side of `??`.

Neat idea. :+1:

4. It supports other `Never`-returning operations, like `abort()` or `exit(_:)` or your custom `usage()` function, on the right side of `??`.

Yep. That’s cool.

5. It supports file-and-line error reporting without having to add any new features; `!!` could not do this because an operator can't have extra, defaulted parameters to carry the file and line.

Erica’s proposal addresses this. Allowing operator functions to accept more than 2 parameters that all have default arguments looks like it would be a pretty straight-forward and non-controversial change. It would also allow the FRP crowd to make their <~ and |> and ಠ_ಠ and (╯°□°)╯︵ ┻━┻ operators all capture the #file and #line they’re used on, which would make debugging those style of apps much easier.

6. It harmonizes with the eventual idea of making `Never` a universal bottom type, but we don't actually have to implement that today, because we can just overload `??` for now.

Yep. That’s cool.

Against these advantages, the only one I can see for `!!` is that it is terse. Terseness is good, especially for a feature which is competing with the single-character postfix `!` operator, but I can't help but be drawn to the flexibility and power of `??` with a `Never` expression on the right-hand side.

I would argue that terseness is a goal of Swift. If it weren’t, we shouldn’t have [Element] as sugar syntax for Array<Element>, or even a bunch of our operators (postfix !, postfix ?, overflow checking operators, nil coalescing, etc).

IMO, having both !! that takes an error message and ?? that takes a () → Never seem like worthwhile goals. The former allows me to be terse, yet still expressive, and the latter allows me the customization to do what I want for whatever is contextually appropriate in my situation.

I don’t think anyone is arguing that ?? () → Never isn’t useful. The purpose of this proposal is to put forward and defend the claim that “!! String” is also useful and meets the barrier to entry in to the Standard Library.

Dave

···

On Jun 29, 2017, at 11:24 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 27, 2017, at 10:16 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

let last = array.last ?? fatalError(“array must not be empty”)

I prefer, the above, explicit version.

-g.

How about?

public func ?? <T>(optional: T?, noreturnOrError: @autoclosure () throws -> Never) rethrows -> T {
    switch optional {
    case .some(let value):
        return value
    case .none:
        try noreturnOrError()
    }
}

Yeah, I saw your email right after I sent mine =)
This works, I tried it and also ran the test suite. There was only one error.

  var s: String = ns ?? "str" as String as String // expected-error{{cannot convert value of type 'NSString?' to expected argument type 'String?'}}
                                                                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                                                                     cannot convert value of type 'String' to expected argument type 'NSString'

I now wonder what the effect on the source compatibility suite would be:

Max

···

On Jun 27, 2017, at 1:03 PM, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:

--
Adrian Zubarev
Sent with Airmail

Am 27. Juni 2017 um 21:54:57, Max Moiseev via swift-evolution (swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:

On Jun 27, 2017, at 10:38 AM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

As you write, this operator becomes sugar for “?? fatalError()” once Never becomes a true bottom type.

In the meantime, can’t the same thing be accomplished by overloading fatalError so it’s a generic function that returns a discardable result of type T, which in turn calls the Never-returning overload?

I like this idea more than adding an extra operator, but overloading fatalError won’t work now because of https://github.com/apple/swift/blob/master/stdlib/public/core/Optional.swift#L668

On Tue, Jun 27, 2017 at 12:25 Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Using an operator to provide feedback on the context of a failed unwrap has become a commonly implemented approach in the Swift developer Community. What are your thoughts about adopting this widely-used operator into the standard library?

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

Details here: bangbang.md · GitHub

Thank you for your thoughtful feedback, -- E

_______________________________________________
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

Also a +1 from me. This is something I always put in to my code.

I agree that having `Never` as a bottom type is interesting, but even if that were the case, the proposed “!!” operator is still useful, because it short-circuits having to fatalError() myself. IE:

let last = array.last ?? fatalError(“array must not be empty”)

The above solution with Never is the better solution IMHO:

No new operator
More flexible: what if you want something else than a fatalError? For example, I might want to define a logAndFatalError() function which stores the error to a log file before trapping.

+1

···

On Jun 27, 2017, at 2:49 PM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

On 27 Jun 2017, at 20:23, Dave DeLong via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

vs

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

I also think having it “fatalError” in shipping code is fine, because it’s equivalent to what would happen if you incorrect force-unwrap.

Dave

On Jun 27, 2017, at 12:08 PM, Zach Waldowski via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Big +1.

Using `!` is widely seen as a smell. However, the need for it still arises; they are preconditions, of a sort. It has become prevalent in my teams’ codebases to do “guard else preconditionFailure”.

I like `!!` over something like `?!`; it follows the pattern that almost every `?` in the language has an equivalent `!`.

The implementation details are ultimately not relevant to me. I’m a fan of `Never` being a bottom type for more reasons than just this. I also see no reason why operator funcs couldn’t accept file and line as extra arguments on the rhs.

Best,
  Zachary Waldowski
  zach@waldowski.me <mailto:zach@waldowski.me>

On Tue, Jun 27, 2017, at 01:16 PM, Erica Sadun via swift-evolution wrote:

Using an operator to provide feedback on the context of a failed unwrap has become a commonly implemented approach in the Swift developer Community. What are your thoughts about adopting this widely-used operator into the standard library?

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

Details here: bangbang.md · GitHub

Thank you for your thoughtful feedback, -- E

_______________________________________________
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

Can’t we simply overload ?? to support @autoclosure () -> Never now and remove that overload when Never is the bottom type? Syntactically it would look and do the same.

···

--
Adrian Zubarev
Sent with Airmail

Am 27. Juni 2017 um 21:52:01, Matthew Johnson via swift-evolution (swift-evolution@swift.org) schrieb:

On Jun 27, 2017, at 2:49 PM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

On 27 Jun 2017, at 20:23, Dave DeLong via swift-evolution <swift-evolution@swift.org> wrote:

Also a +1 from me. This is something I always put in to my code.

I agree that having `Never` as a bottom type is interesting, but even if that were the case, the proposed “!!” operator is still useful, because it short-circuits having to fatalError() myself. IE:

let last = array.last ?? fatalError(“array must not be empty”)

The above solution with Never is the better solution IMHO:

No new operator
More flexible: what if you want something else than a fatalError? For example, I might want to define a logAndFatalError() function which stores the error to a log file before trapping.

+1

vs

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

I also think having it “fatalError” in shipping code is fine, because it’s equivalent to what would happen if you incorrect force-unwrap.

Dave

On Jun 27, 2017, at 12:08 PM, Zach Waldowski via swift-evolution <swift-evolution@swift.org> wrote:

Big +1.

Using `!` is widely seen as a smell. However, the need for it still arises; they are preconditions, of a sort. It has become prevalent in my teams’ codebases to do “guard else preconditionFailure”.

I like `!!` over something like `?!`; it follows the pattern that almost every `?` in the language has an equivalent `!`.

The implementation details are ultimately not relevant to me. I’m a fan of `Never` being a bottom type for more reasons than just this. I also see no reason why operator funcs couldn’t accept file and line as extra arguments on the rhs.

Best,
Zachary Waldowski
zach@waldowski.me

On Tue, Jun 27, 2017, at 01:16 PM, Erica Sadun via swift-evolution wrote:
Using an operator to provide feedback on the context of a failed unwrap has become a commonly implemented approach in the Swift developer Community. What are your thoughts about adopting this widely-used operator into the standard library?

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

Details here: bangbang.md · GitHub

Thank you for your thoughtful feedback, -- E

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

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

_______________________________________________
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

This solution is nifty indeed, and has the chief advantage of working.

···

On Tue, Jun 27, 2017 at 16:55 Max Moiseev via swift-evolution < swift-evolution@swift.org> wrote:

On Jun 27, 2017, at 1:03 PM, Adrian Zubarev < > adrian.zubarev@devandartist.com> wrote:

How about?

public func ?? <T>(optional: T?, noreturnOrError: @autoclosure () throws -> Never) rethrows -> T {
    switch optional {
    case .some(let value):
        return value
    case .none:
        try noreturnOrError()
    }
}

Yeah, I saw your email right after I sent mine =)
This works, I tried it and also ran the test suite. There was only one
error.

  var s: String = ns ?? "str" as String as String //
expected-error{{cannot convert value of type 'NSString?' to expected
argument type 'String?'}}

^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

cannot convert value of type 'String' to expected argument type 'NSString'

I now wonder what the effect on the source compatibility suite would be:
[NOT FOR MERGE][stdlib] Adding a variant of ??, with a Never returning closure by moiseev · Pull Request #10639 · apple/swift · GitHub

Max

--
Adrian Zubarev
Sent with Airmail

Am 27. Juni 2017 um 21:54:57, Max Moiseev via swift-evolution (
swift-evolution@swift.org) schrieb:

On Jun 27, 2017, at 10:38 AM, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

As you write, this operator becomes sugar for “?? fatalError()” once Never
becomes a true bottom type.

In the meantime, can’t the same thing be accomplished by overloading
fatalError so it’s a generic function that returns a discardable result of
type T, which in turn calls the Never-returning overload?

I like this idea more than adding an extra operator, but overloading
fatalError won’t work now because of
https://github.com/apple/swift/blob/master/stdlib/public/core/Optional.swift#L668

On Tue, Jun 27, 2017 at 12:25 Erica Sadun via swift-evolution < > swift-evolution@swift.org> wrote:

Using an operator to provide feedback on the context of a failed unwrap
has become a commonly implemented approach in the Swift developer
Community. What are your thoughts about adopting this widely-used operator
into the standard library?

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

Details here:
bangbang.md · GitHub

Thank you for your thoughtful feedback, -- E

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

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

_______________________________________________
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

How would the line run then? Would it simply act as a forced unwrapped under -Ounchecked?

-- E

···

On Jun 28, 2017, at 3:52 AM, Yuta Koshizawa via swift-evolution <swift-evolution@swift.org> wrote:

Hi, I think it is an orthogonal issue if we need a new operator. It is
also possible to introduce an infix `!` for it.

I am sure that we do not need to avoid `precondition` as long as we
use it appropriately. It is useful to realize consistent behavior with
`Array`'s `subscript`, forced unwrapping `!`, `&+` and so on. In this
context, `precondition` does not mean a general word "precondition"
but the `precondition` function in the Swift standard library, which
is removed when -Ounchecked.

Hi, I think it is an orthogonal issue if we need a new operator. It is
also possible to introduce an infix `!` for it.

I am sure that we do not need to avoid `precondition` as long as we
use it appropriately. It is useful to realize consistent behavior with
`Array`'s `subscript`, forced unwrapping `!`, `&+` and so on. In this
context, `precondition` does not mean a general word "precondition"
but the `precondition` function in the Swift standard library, which
is removed when -Ounchecked.

How would the line run then? Would it simply act as a forced unwrapped under -Ounchecked?

From the docs:

"In -Ounchecked builds, the optimizer may assume that this function is never called. Failure to satisfy that assumption is a serious programming error.”

Aka, v. bad things happen if the precondition does not hold.

···

On Jun 28, 2017, at 7:47 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 28, 2017, at 3:52 AM, Yuta Koshizawa via swift-evolution <swift-evolution@swift.org> wrote:

-- E

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

I have been persuaded that extending the capabilities of the current ?? operator has far more advantages than adding a new limited !! operator. While I initially did not like the usage of the generally-assumed-safe ? for throwing operations, the clarity provided by having to explicitly state your ‘fatalError’ or ‘preconditionFailure’ etc, does make it obvious about what is going on.

Also, considering how this capability would eventually be possible with ?? anyways once Never become a true bottom type, it does not make sense to add a new operator that would essentially become deprecated in a short matter of time anyways. As many people have stated, the bar for additions to the Swift language should be, and is, exceptionally high. There just aren’t enough pro’s for the !! operator to make it worthwhile.

···

On Jun 30, 2017, at 9:31 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

These are all excellent points. I also feel they sidestep the motivation of the proposal. Even if there were a bottom `Never` and you could use `?? fatalError()`, I still think the language would benefit from `!!`.

As the language is right now, you can write your own "or die" function using a `guard` statement, overloading `??`, or implementing `!!`. Being able to duplicate an explanatory fail path isn't the point. Offering a best-practices alternative to `!!` that is easy to use, obvious to understand, and simple to adopt is.

As for the `#line` and `#file` issue, that is my biggest concern but I believe that can be surmounted.

-- E

On Jun 29, 2017, at 11:23 PM, Brent Royal-Gordon <brent@architechies.com <mailto:brent@architechies.com>> wrote:

On Jun 27, 2017, at 10:16 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Using an operator to provide feedback on the context of a failed unwrap has become a commonly implemented approach in the Swift developer Community. What are your thoughts about adopting this widely-used operator into the standard library?

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

Details here: bangbang.md · GitHub

Thank you for your thoughtful feedback, -- E

Finally found a few minutes to read this thread.

I'm a big fan of the `Never`-based approach. (I was before, but I am more so now.) Here are the points I can see in its favor:

1. It is extremely clear about what's happening—`!!` is another random operator to learn, but `fatalError(_:)` or `preconditionFailure(_:)` are fairly self-explanatory, and `??` is something you might already be using.

2. It allows you to control the optimization behavior by using `fatalError`, `preconditionFailure`, or `assertionFailure` as desired.

3. If we later change `throw` from being a statement to being a `Never`-returning expression, you could use `throw` on the right-hand side of `??`.

4. It supports other `Never`-returning operations, like `abort()` or `exit(_:)` or your custom `usage()` function, on the right side of `??`.

5. It supports file-and-line error reporting without having to add any new features; `!!` could not do this because an operator can't have extra, defaulted parameters to carry the file and line.

6. It harmonizes with the eventual idea of making `Never` a universal bottom type, but we don't actually have to implement that today, because we can just overload `??` for now.

Against these advantages, the only one I can see for `!!` is that it is terse. Terseness is good, especially for a feature which is competing with the single-character postfix `!` operator, but I can't help but be drawn to the flexibility and power of `??` with a `Never` expression on the right-hand side.

--
Brent Royal-Gordon
Architechies

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

"In -Ounchecked builds, the optimizer may assume that this function is
never called. Failure to satisfy that assumption is a serious programming
error.”

Yes, and so the following `!!` can perform identically to `!` in
-Ounchecked builds when it is inlined.

public static func !!(optional: Optional, errorMessage: @autoclosure () ->
String) -> Wrapped { precondition(optional != nil, errorMessage()) return
optional! }

···

--
Yuta

2017-06-29 6:42 GMT+09:00 Jaden Geller <jaden.geller@gmail.com>:

> On Jun 28, 2017, at 7:47 AM, Erica Sadun via swift-evolution < > swift-evolution@swift.org> wrote:
>
>
>> On Jun 28, 2017, at 3:52 AM, Yuta Koshizawa via swift-evolution < > swift-evolution@swift.org> wrote:
>>
>> Hi, I think it is an orthogonal issue if we need a new operator. It is
>> also possible to introduce an infix `!` for it.
>>
>> I am sure that we do not need to avoid `precondition` as long as we
>> use it appropriately. It is useful to realize consistent behavior with
>> `Array`'s `subscript`, forced unwrapping `!`, `&+` and so on. In this
>> context, `precondition` does not mean a general word "precondition"
>> but the `precondition` function in the Swift standard library, which
>> is removed when -Ounchecked.
>
>
> How would the line run then? Would it simply act as a forced unwrapped
under -Ounchecked?

From the docs:

"In -Ounchecked builds, the optimizer may assume that this function is
never called. Failure to satisfy that assumption is a serious programming
error.”

Aka, v. bad things happen if the precondition does not hold.

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

`throw` is currently a statement. Imagine, for sake of illustration, that it was instead a function. This function would take an `Error` as a parameter and throw it. It would never return normally—it would only return by throwing—so its return type would be `Never`:

  @_implicitlyTry
  func throw(_ error: Error) throws -> Never {
    try Builtin.throw(error)
    unreachable()
  }

What I'm suggesting is that `throw` should remain a keyword, but should have the semantics of this `throw(_:)` function. The parser should allow it in expression context, the `try` checker should treat it as though it was already marked `try`, and the type checker should treat it as an expression that returns `Never` but can throw.

That would then allow you to say things like:

  let lastItem = array.last ?? throw MyError.arrayEmpty

It would not have any negative effect I can think of on `Result`. In fact, trying to directly wrap a `throw SomeError.foo` statement in a `Result` would produce a `Result<Never, SomeError>`, correctly expressing the fact that the result of that particular expression can never be successful.

···

On Jun 30, 2017, at 5:38 AM, Matthew Johnson <matthew@anandabits.com> wrote:

3. If we later change `throw` from being a statement to being a `Never`-returning expression, you could use `throw` on the right-hand side of `??`.

What do you have in mind here? I don't recall any discussion of `throw` return Never. It seems like a novel use of a bottom type that might preclude the possibility of ever having a Result type that seamlessly bridges to Swift's error handling.

--
Brent Royal-Gordon
Architechies

Just wanted to throw in my preference for supporting both options. I'd also like to emphasize that !! isn't appealing just for the sake of terseness, but also because it completes the ?, ??, !, !! "family" of unwrapping operators in a way that reinforces their underlying concepts. In that sense, I think the addition of the operator actually makes the overall system more learnable through repeated application of a pattern.

Jarod

···

On Jun 30, 2017, 10:02 -0700, Dave DeLong via swift-evolution <swift-evolution@swift.org>, wrote:

These are good points, Brent.

> On Jun 29, 2017, at 11:24 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:
>
> > On Jun 27, 2017, at 10:16 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:
> >
> > Using an operator to provide feedback on the context of a failed unwrap has become a commonly implemented approach in the Swift developer Community. What are your thoughts about adopting this widely-used operator into the standard library?
> >
> > guard !lastItem.isEmpty else { return }
> > let lastItem = array.last !! "Array must be non-empty"
> >
> > Details here: bangbang.md · GitHub
> >
> > Thank you for your thoughtful feedback, -- E
>
> Finally found a few minutes to read this thread.
>
> I'm a big fan of the `Never`-based approach. (I was before, but I am more so now.) Here are the points I can see in its favor:
>
> 1. It is extremely clear about what's happening—`!!` is another random operator to learn, but `fatalError(_:)` or `preconditionFailure(_:)` are fairly self-explanatory, and `??` is something you might already be using.

!! is also clear about what’s happening, because it’s just like the existing ! operator.

> 2. It allows you to control the optimization behavior by using `fatalError`, `preconditionFailure`, or `assertionFailure` as desired.

Yep. That’s cool.

> 3. If we later change `throw` from being a statement to being a `Never`-returning expression, you could use `throw` on the right-hand side of `??`.

Neat idea. :+1:

> 4. It supports other `Never`-returning operations, like `abort()` or `exit(_:)` or your custom `usage()` function, on the right side of `??`.

Yep. That’s cool.

> 5. It supports file-and-line error reporting without having to add any new features; `!!` could not do this because an operator can't have extra, defaulted parameters to carry the file and line.

Erica’s proposal addresses this. Allowing operator functions to accept more than 2 parameters that all have default arguments looks like it would be a pretty straight-forward and non-controversial change. It would also allow the FRP crowd to make their <~ and |> and ಠ_ಠ and (╯°□°)╯︵ ┻━┻ operators all capture the #file and #line they’re used on, which would make debugging those style of apps much easier.

> 6. It harmonizes with the eventual idea of making `Never` a universal bottom type, but we don't actually have to implement that today, because we can just overload `??` for now.

Yep. That’s cool.

> Against these advantages, the only one I can see for `!!` is that it is terse. Terseness is good, especially for a feature which is competing with the single-character postfix `!` operator, but I can't help but be drawn to the flexibility and power of `??` with a `Never` expression on the right-hand side.

I would argue that terseness is a goal of Swift. If it weren’t, we shouldn’t have [Element] as sugar syntax for Array<Element>, or even a bunch of our operators (postfix !, postfix ?, overflow checking operators, nil coalescing, etc).

IMO, having both !! that takes an error message and ?? that takes a () → Never seem like worthwhile goals. The former allows me to be terse, yet still expressive, and the latter allows me the customization to do what I want for whatever is contextually appropriate in my situation.

I don’t think anyone is arguing that ?? () → Never isn’t useful. The purpose of this proposal is to put forward and defend the claim that “!! String” is also useful and meets the barrier to entry in to the Standard Library.

Dave

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

100% agree with everything. A custom operator is a good idea for operations that are actually frequent: once you learn what it means, it really helps reduce noise in code and improve readability. But forced unwrapping is something that's intended to be used sparsely.

Elviro

···

Il giorno 30 giu 2017, alle ore 10:15, Víctor Pimentel Rodríguez via swift-evolution <swift-evolution@swift.org> ha scritto:

On Fri, Jun 30, 2017 at 7:24 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jun 27, 2017, at 10:16 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Using an operator to provide feedback on the context of a failed unwrap has become a commonly implemented approach in the Swift developer Community. What are your thoughts about adopting this widely-used operator into the standard library?

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

Details here: bangbang.md · GitHub

Thank you for your thoughtful feedback, -- E

Finally found a few minutes to read this thread.

I'm a big fan of the `Never`-based approach. (I was before, but I am more so now.) Here are the points I can see in its favor:

1. It is extremely clear about what's happening—`!!` is another random operator to learn, but `fatalError(_:)` or `preconditionFailure(_:)` are fairly self-explanatory, and `??` is something you might already be using.

2. It allows you to control the optimization behavior by using `fatalError`, `preconditionFailure`, or `assertionFailure` as desired.

3. If we later change `throw` from being a statement to being a `Never`-returning expression, you could use `throw` on the right-hand side of `??`.

4. It supports other `Never`-returning operations, like `abort()` or `exit(_:)` or your custom `usage()` function, on the right side of `??`.

5. It supports file-and-line error reporting without having to add any new features; `!!` could not do this because an operator can't have extra, defaulted parameters to carry the file and line.

6. It harmonizes with the eventual idea of making `Never` a universal bottom type, but we don't actually have to implement that today, because we can just overload `??` for now.

Against these advantages, the only one I can see for `!!` is that it is terse. Terseness is good, especially for a feature which is competing with the single-character postfix `!` operator, but I can't help but be drawn to the flexibility and power of `??` with a `Never` expression on the right-hand side.

+1 to everything.

If terseness is the only clear advantage of a new operator that is meant to be used sparsely, it's clear to me that the ?? Never form is better for the Swift community as a whole.

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

The compatibility testing revealed no related errors. And the full test suite only shows one that I listed already.

Max

···

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

This solution is nifty indeed, and has the chief advantage of working.
On Tue, Jun 27, 2017 at 16:55 Max Moiseev via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jun 27, 2017, at 1:03 PM, Adrian Zubarev <adrian.zubarev@devandartist.com <mailto:adrian.zubarev@devandartist.com>> wrote:

How about?

public func ?? <T>(optional: T?, noreturnOrError: @autoclosure () throws -> Never) rethrows -> T {
    switch optional {
    case .some(let value):
        return value
    case .none:
        try noreturnOrError()
    }
}

Yeah, I saw your email right after I sent mine =)
This works, I tried it and also ran the test suite. There was only one error.

  var s: String = ns ?? "str" as String as String // expected-error{{cannot convert value of type 'NSString?' to expected argument type 'String?'}}
                                                                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                                                                     cannot convert value of type 'String' to expected argument type 'NSString'

I now wonder what the effect on the source compatibility suite would be:
[NOT FOR MERGE][stdlib] Adding a variant of ??, with a Never returning closure by moiseev · Pull Request #10639 · apple/swift · GitHub

Max

--
Adrian Zubarev
Sent with Airmail

Am 27. Juni 2017 um 21:54:57, Max Moiseev via swift-evolution (swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:

On Jun 27, 2017, at 10:38 AM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

As you write, this operator becomes sugar for “?? fatalError()” once Never becomes a true bottom type.

In the meantime, can’t the same thing be accomplished by overloading fatalError so it’s a generic function that returns a discardable result of type T, which in turn calls the Never-returning overload?

I like this idea more than adding an extra operator, but overloading fatalError won’t work now because of https://github.com/apple/swift/blob/master/stdlib/public/core/Optional.swift#L668

On Tue, Jun 27, 2017 at 12:25 Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Using an operator to provide feedback on the context of a failed unwrap has become a commonly implemented approach in the Swift developer Community. What are your thoughts about adopting this widely-used operator into the standard library?

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

Details here: bangbang.md · GitHub

Thank you for your thoughtful feedback, -- E

_______________________________________________
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

I'll give this a kick around as soon as I get a moment and revise. I am slightly concerned that we discussed variations of this in the past (throwing if memory serves, with `Error` on the rhs) and that it broke the expectations of nil-coalescing.

In general, does everyone prefer `?? () -> Never` or `!! () -> Never`? I can argue both ways, with the goal in reading code as "unwrap or die".

-- E

···

On Jun 27, 2017, at 7:16 PM, Max Moiseev via swift-evolution <swift-evolution@swift.org> wrote:

The compatibility testing revealed no related errors. And the full test suite only shows one that I listed already.

Max

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

This solution is nifty indeed, and has the chief advantage of working.
On Tue, Jun 27, 2017 at 16:55 Max Moiseev via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jun 27, 2017, at 1:03 PM, Adrian Zubarev <adrian.zubarev@devandartist.com <mailto:adrian.zubarev@devandartist.com>> wrote:

How about?

public func ?? <T>(optional: T?, noreturnOrError: @autoclosure () throws -> Never) rethrows -> T {
    switch optional {
    case .some(let value):
        return value
    case .none:
        try noreturnOrError()
    }
}

Yeah, I saw your email right after I sent mine =)
This works, I tried it and also ran the test suite. There was only one error.

  var s: String = ns ?? "str" as String as String // expected-error{{cannot convert value of type 'NSString?' to expected argument type 'String?'}}
                                                                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                                                                     cannot convert value of type 'String' to expected argument type 'NSString'

I now wonder what the effect on the source compatibility suite would be:
[NOT FOR MERGE][stdlib] Adding a variant of ??, with a Never returning closure by moiseev · Pull Request #10639 · apple/swift · GitHub

Max

--
Adrian Zubarev
Sent with Airmail

Am 27. Juni 2017 um 21:54:57, Max Moiseev via swift-evolution (swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:

On Jun 27, 2017, at 10:38 AM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

As you write, this operator becomes sugar for “?? fatalError()” once Never becomes a true bottom type.

In the meantime, can’t the same thing be accomplished by overloading fatalError so it’s a generic function that returns a discardable result of type T, which in turn calls the Never-returning overload?

I like this idea more than adding an extra operator, but overloading fatalError won’t work now because of https://github.com/apple/swift/blob/master/stdlib/public/core/Optional.swift#L668

On Tue, Jun 27, 2017 at 12:25 Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Using an operator to provide feedback on the context of a failed unwrap has become a commonly implemented approach in the Swift developer Community. What are your thoughts about adopting this widely-used operator into the standard library?

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

Details here: bangbang.md · GitHub

Thank you for your thoughtful feedback, -- E

_______________________________________________
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