Nil-rejection operator


(Jack Newcombe) #1

Hi all,

Currently there are a number of different operators for dealing with optionals that cover most of the use cases. However, I think I’ve identified a missing complement for the existing operators for optionals.

Take the following outcomes for interacting with an optional using existing operators (!, ?, ??). The outcomes of using these are as follows:

- value? :
  if value is nil, do nothing and return nil
  if value is not nil, complete the chain by evaluating the rest of the expression. Return the result of the expression
- value! :
  if value is nil, throw.a fatal error.
  If value is not nil, complete the chain by evaluating the rest of the expression. Return the result of the expression
- value ?? default :
  if value is nil, return default
  if value is not nil, return value

It seems to me that, if it is possible to coalesce a nil value with a default value, it should also be possible to reject a nil value a non-fatal error.

I propose the introduction of a nil-rejection operator (represented here as !!) as a complement to the above operators.
.
This operator should allow an equivalent behaviour to the forced unwrapping of a variable, but with the provision of an error to throw in place of throwing a fatal error.

- value !! Error :
  if value is nil, throw non-fatal error
  if value is not nil, return value

Example of how this syntax might work (Where CustomError: Error):

  let value = try optionalValue !! CustomError.failure

It is possible to implement this in Swift 3 with the following declaration:

  infix operator !! : NilCoalescingPrecedence

  func !!<UnwrappedType: Any, ErrorType: Error>(lhs: Optional<UnwrappedType>, rhs: ErrorType) throws -> UnwrappedType {
      guard let unwrappedValue = lhs else {
          throw rhs
      }
      return unwrappedValue
  }

I’ve added further examples including composition with the nil-coalescence operator here:
https://gist.github.com/jnewc/304bdd2d330131ddb8a1e615ee560d1d

This would be particularly convenient in cases where a functions expects significant number of optional to contain non-nil values, without the need to repeat non-generic guard-let structures with the same else code-block.

Best regards,

Jack


(Jean-Daniel) #2

While I find the concept interesting, I give a big -1 for using the ‘!’ operator for something else that fatal error.

···

Le 8 févr. 2017 à 21:00, Jack Newcombe via swift-evolution <swift-evolution@swift.org> a écrit :

Hi all,

Currently there are a number of different operators for dealing with optionals that cover most of the use cases. However, I think I’ve identified a missing complement for the existing operators for optionals.

Take the following outcomes for interacting with an optional using existing operators (!, ?, ??). The outcomes of using these are as follows:

- value? :
  if value is nil, do nothing and return nil
  if value is not nil, complete the chain by evaluating the rest of the expression. Return the result of the expression
- value! :
  if value is nil, throw.a fatal error.
  If value is not nil, complete the chain by evaluating the rest of the expression. Return the result of the expression
- value ?? default :
  if value is nil, return default
  if value is not nil, return value

It seems to me that, if it is possible to coalesce a nil value with a default value, it should also be possible to reject a nil value a non-fatal error.

I propose the introduction of a nil-rejection operator (represented here as !!) as a complement to the above operators.
.
This operator should allow an equivalent behaviour to the forced unwrapping of a variable, but with the provision of an error to throw in place of throwing a fatal error.

- value !! Error :
  if value is nil, throw non-fatal error
  if value is not nil, return value

Example of how this syntax might work (Where CustomError: Error):

  let value = try optionalValue !! CustomError.failure

It is possible to implement this in Swift 3 with the following declaration:

  infix operator !! : NilCoalescingPrecedence

  func !!<UnwrappedType: Any, ErrorType: Error>(lhs: Optional<UnwrappedType>, rhs: ErrorType) throws -> UnwrappedType {
      guard let unwrappedValue = lhs else {
          throw rhs
      }
      return unwrappedValue
  }

I’ve added further examples including composition with the nil-coalescence operator here:
https://gist.github.com/jnewc/304bdd2d330131ddb8a1e615ee560d1d

This would be particularly convenient in cases where a functions expects significant number of optional to contain non-nil values, without the need to repeat non-generic guard-let structures with the same else code-block.

Best regards,

Jack

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


(Brent Royal-Gordon) #3

Rather than invent a new operator, I'd prefer to make `throw` an expression rather than a statement. Then you could write:

  let value = optionalValue ?? throw CustomError.Failure

One issue here would be figuring out the proper return type for `throw`. Although if `Never` were a subtype-of-all-types, that would of course work. :^)

···

On Feb 8, 2017, at 12:00 PM, Jack Newcombe via swift-evolution <swift-evolution@swift.org> wrote:

I propose the introduction of a nil-rejection operator (represented here as !!) as a complement to the above operators.
.
This operator should allow an equivalent behaviour to the forced unwrapping of a variable, but with the provision of an error to throw in place of throwing a fatal error.

- value !! Error :
  if value is nil, throw non-fatal error
  if value is not nil, return value

Example of how this syntax might work (Where CustomError: Error):

  let value = try optionalValue !! CustomError.failure

--
Brent Royal-Gordon
Architechies


(Jack Newcombe) #4

Hi all,

Now that phase 2 has begun, am I able to submit a proposal for this?

Best regards,

Jack

···

On 8 Feb 2017, at 20:00, Jack Newcombe <jack@newcombe.io> wrote:

Hi all,

Currently there are a number of different operators for dealing with optionals that cover most of the use cases. However, I think I’ve identified a missing complement for the existing operators for optionals.

Take the following outcomes for interacting with an optional using existing operators (!, ?, ??). The outcomes of using these are as follows:

- value? :
  if value is nil, do nothing and return nil
  if value is not nil, complete the chain by evaluating the rest of the expression. Return the result of the expression
- value! :
  if value is nil, throw.a fatal error.
  If value is not nil, complete the chain by evaluating the rest of the expression. Return the result of the expression
- value ?? default :
  if value is nil, return default
  if value is not nil, return value

It seems to me that, if it is possible to coalesce a nil value with a default value, it should also be possible to reject a nil value a non-fatal error.

I propose the introduction of a nil-rejection operator (represented here as !!) as a complement to the above operators.
.
This operator should allow an equivalent behaviour to the forced unwrapping of a variable, but with the provision of an error to throw in place of throwing a fatal error.

- value !! Error :
  if value is nil, throw non-fatal error
  if value is not nil, return value

Example of how this syntax might work (Where CustomError: Error):

  let value = try optionalValue !! CustomError.failure

It is possible to implement this in Swift 3 with the following declaration:

  infix operator !! : NilCoalescingPrecedence

  func !!<UnwrappedType: Any, ErrorType: Error>(lhs: Optional<UnwrappedType>, rhs: ErrorType) throws -> UnwrappedType {
      guard let unwrappedValue = lhs else {
          throw rhs
      }
      return unwrappedValue
  }

I’ve added further examples including composition with the nil-coalescence operator here:
https://gist.github.com/jnewc/304bdd2d330131ddb8a1e615ee560d1d

This would be particularly convenient in cases where a functions expects significant number of optional to contain non-nil values, without the need to repeat non-generic guard-let structures with the same else code-block.

Best regards,

Jack


(Xiaodi Wu) #5

This seems to boil down to sugar where `guard let foo = ... else { throw
... }` is spelled `let foo = try ... !! ...`.

While the analysis is interesting I don't see that this is an obvious win
sufficient for the standard library. As you show it's possible to create
for yourself.

···

On Wed, Feb 8, 2017 at 14:20 Jean-Daniel via swift-evolution < swift-evolution@swift.org> wrote:

While I find the concept interesting, I give a big -1 for using the ‘!’
operator for something else that fatal error.

Le 8 févr. 2017 à 21:00, Jack Newcombe via swift-evolution < > swift-evolution@swift.org> a écrit :

Hi all,

Currently there are a number of different operators for dealing with
optionals that cover most of the use cases. However, I think I’ve
identified a missing complement for the existing operators for optionals.

Take the following outcomes for interacting with an optional using
existing operators (!, ?, ??). The outcomes of using these are as follows:

- value? :
if value is nil, do nothing and return nil
if value is not nil, complete the chain by evaluating the rest of the
expression. Return the result of the expression
- value! :
if value is nil, throw.a fatal error.
If value is not nil, complete the chain by evaluating the rest of the
expression. Return the result of the expression
- value ?? default :
if value is nil, return default
if value is not nil, return value

It seems to me that, if it is possible to coalesce a nil value with a
default value, it should also be possible to reject a nil value a non-fatal
error.

I propose the introduction of a nil-rejection operator (represented here
as !!) as a complement to the above operators.
.
This operator should allow an equivalent behaviour to the forced
unwrapping of a variable, but with the provision of an error to throw in
place of throwing a fatal error.

- value !! Error :
if value is nil, throw non-fatal error
if value is not nil, return value

Example of how this syntax might work (Where CustomError: Error):

let value = try optionalValue !! CustomError.failure

It is possible to implement this in Swift 3 with the following declaration:

infix operator !! : NilCoalescingPrecedence

func !!<UnwrappedType: Any, ErrorType: Error>(lhs: Optional<UnwrappedType>, rhs: ErrorType) throws -> UnwrappedType
{
    guard let unwrappedValue = lhs else {
        throw rhs
    }
    return unwrappedValue
}

I’ve added further examples including composition with the nil-coalescence
operator here:
https://gist.github.com/jnewc/304bdd2d330131ddb8a1e615ee560d1d

This would be particularly convenient in cases where a functions expects
significant number of optional to contain non-nil values, without the need
to repeat non-generic guard-let structures with the same else code-block.

Best regards,

Jack

_______________________________________________
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


(Jack Newcombe) #6

I picked !! because it seemed to follow somewhat naturally from force-unwrap and nil-coalescing; however, that does depend on how you interpret the syntax of the operator.

I interpreted ?? to figuratively mean “? with a default result instead of nil”, and so derived !! As figuratively meaning “! with default error instead of fatal error”.

Regardless, I’m definitely open to alternatives, although can’t think of anything more appropriate offhand.

···

On 8 Feb 2017, at 20:20, Jean-Daniel <mailing@xenonium.com> wrote:

While I find the concept interesting, I give a big -1 for using the ‘!’ operator for something else that fatal error.

Le 8 févr. 2017 à 21:00, Jack Newcombe via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

Hi all,

Currently there are a number of different operators for dealing with optionals that cover most of the use cases. However, I think I’ve identified a missing complement for the existing operators for optionals.

Take the following outcomes for interacting with an optional using existing operators (!, ?, ??). The outcomes of using these are as follows:

- value? :
  if value is nil, do nothing and return nil
  if value is not nil, complete the chain by evaluating the rest of the expression. Return the result of the expression
- value! :
  if value is nil, throw.a fatal error.
  If value is not nil, complete the chain by evaluating the rest of the expression. Return the result of the expression
- value ?? default :
  if value is nil, return default
  if value is not nil, return value

It seems to me that, if it is possible to coalesce a nil value with a default value, it should also be possible to reject a nil value a non-fatal error.

I propose the introduction of a nil-rejection operator (represented here as !!) as a complement to the above operators.
.
This operator should allow an equivalent behaviour to the forced unwrapping of a variable, but with the provision of an error to throw in place of throwing a fatal error.

- value !! Error :
  if value is nil, throw non-fatal error
  if value is not nil, return value

Example of how this syntax might work (Where CustomError: Error):

  let value = try optionalValue !! CustomError.failure

It is possible to implement this in Swift 3 with the following declaration:

  infix operator !! : NilCoalescingPrecedence

  func !!<UnwrappedType: Any, ErrorType: Error>(lhs: Optional<UnwrappedType>, rhs: ErrorType) throws -> UnwrappedType {
      guard let unwrappedValue = lhs else {
          throw rhs
      }
      return unwrappedValue
  }

I’ve added further examples including composition with the nil-coalescence operator here:
https://gist.github.com/jnewc/304bdd2d330131ddb8a1e615ee560d1d

This would be particularly convenient in cases where a functions expects significant number of optional to contain non-nil values, without the need to repeat non-generic guard-let structures with the same else code-block.

Best regards,

Jack

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


(Jack Newcombe) #7

It’s absolutely true that this is syntactic sugar, but then so is nil-coalescing where "x ?? y” is syntactic sugar for “x != nil ? x : y”.

You can also similarly recreate the nil-coalescing operator in Swift yourself, so I’m not sure that’s a strong argument for any operator being or not being in the standard library.

···

On 8 Feb 2017, at 20:29, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

This seems to boil down to sugar where `guard let foo = ... else { throw ... }` is spelled `let foo = try ... !! ...`.

While the analysis is interesting I don't see that this is an obvious win sufficient for the standard library. As you show it's possible to create for yourself.
On Wed, Feb 8, 2017 at 14:20 Jean-Daniel via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
While I find the concept interesting, I give a big -1 for using the ‘!’ operator for something else that fatal error.

Le 8 févr. 2017 à 21:00, Jack Newcombe via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

Hi all,

Currently there are a number of different operators for dealing with optionals that cover most of the use cases. However, I think I’ve identified a missing complement for the existing operators for optionals.

Take the following outcomes for interacting with an optional using existing operators (!, ?, ??). The outcomes of using these are as follows:

- value? :
  if value is nil, do nothing and return nil
  if value is not nil, complete the chain by evaluating the rest of the expression. Return the result of the expression
- value! :
  if value is nil, throw.a fatal error.
  If value is not nil, complete the chain by evaluating the rest of the expression. Return the result of the expression
- value ?? default :
  if value is nil, return default
  if value is not nil, return value

It seems to me that, if it is possible to coalesce a nil value with a default value, it should also be possible to reject a nil value a non-fatal error.

I propose the introduction of a nil-rejection operator (represented here as !!) as a complement to the above operators.
.
This operator should allow an equivalent behaviour to the forced unwrapping of a variable, but with the provision of an error to throw in place of throwing a fatal error.

- value !! Error :
  if value is nil, throw non-fatal error
  if value is not nil, return value

Example of how this syntax might work (Where CustomError: Error):

  let value = try optionalValue !! CustomError.failure

It is possible to implement this in Swift 3 with the following declaration:

  infix operator !! : NilCoalescingPrecedence

  func !!<UnwrappedType: Any, ErrorType: Error>(lhs: Optional<UnwrappedType>, rhs: ErrorType) throws -> UnwrappedType {
      guard let unwrappedValue = lhs else {
          throw rhs
      }
      return unwrappedValue
  }

I’ve added further examples including composition with the nil-coalescence operator here:
https://gist.github.com/jnewc/304bdd2d330131ddb8a1e615ee560d1d

This would be particularly convenient in cases where a functions expects significant number of optional to contain non-nil values, without the need to repeat non-generic guard-let structures with the same else code-block.

Best regards,

Jack

_______________________________________________
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


(Xiaodi Wu) #8

As has been mentioned by the core team, syntactic sugar is not in scope for
this phase of Swift 4 evolution and was said to be the lowest priority for
the next. The bar for adding a new operator is going to be higher than for
justifying the continued existence of an existing one.

That said, the nil coalescing operator has problems of its own that have
been raised on this list, mostly to do with sometimes unintuitive behavior
as a result of its associativity and precedence.

···

On Wed, Feb 8, 2017 at 14:35 Jack Newcombe <jack@newcombe.io> wrote:

It’s absolutely true that this is syntactic sugar, but then so is
nil-coalescing where "x ?? y” is syntactic sugar for “x != nil ? x : y”.

You can also similarly recreate the nil-coalescing operator in Swift
yourself, so I’m not sure that’s a strong argument for any operator being
or not being in the standard library.

On 8 Feb 2017, at 20:29, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

This seems to boil down to sugar where `guard let foo = ... else { throw
... }` is spelled `let foo = try ... !! ...`.

While the analysis is interesting I don't see that this is an obvious win
sufficient for the standard library. As you show it's possible to create
for yourself.
On Wed, Feb 8, 2017 at 14:20 Jean-Daniel via swift-evolution < > swift-evolution@swift.org> wrote:

While I find the concept interesting, I give a big -1 for using the ‘!’
operator for something else that fatal error.

Le 8 févr. 2017 à 21:00, Jack Newcombe via swift-evolution < > swift-evolution@swift.org> a écrit :

Hi all,

Currently there are a number of different operators for dealing with
optionals that cover most of the use cases. However, I think I’ve
identified a missing complement for the existing operators for optionals.

Take the following outcomes for interacting with an optional using
existing operators (!, ?, ??). The outcomes of using these are as follows:

- value? :
if value is nil, do nothing and return nil
if value is not nil, complete the chain by evaluating the rest of the
expression. Return the result of the expression
- value! :
if value is nil, throw.a fatal error.
If value is not nil, complete the chain by evaluating the rest of the
expression. Return the result of the expression
- value ?? default :
if value is nil, return default
if value is not nil, return value

It seems to me that, if it is possible to coalesce a nil value with a
default value, it should also be possible to reject a nil value a non-fatal
error.

I propose the introduction of a nil-rejection operator (represented here
as !!) as a complement to the above operators.
.
This operator should allow an equivalent behaviour to the forced
unwrapping of a variable, but with the provision of an error to throw in
place of throwing a fatal error.

- value !! Error :
if value is nil, throw non-fatal error
if value is not nil, return value

Example of how this syntax might work (Where CustomError: Error):

let value = try optionalValue !! CustomError.failure

It is possible to implement this in Swift 3 with the following declaration:

infix operator !! : NilCoalescingPrecedence

func !!<UnwrappedType: Any, ErrorType: Error>(lhs: Optional<UnwrappedType>, rhs: ErrorType) throws -> UnwrappedType
{
    guard let unwrappedValue = lhs else {
        throw rhs
    }
    return unwrappedValue
}

I’ve added further examples including composition with the nil-coalescence
operator here:
https://gist.github.com/jnewc/304bdd2d330131ddb8a1e615ee560d1d

This would be particularly convenient in cases where a functions expects
significant number of optional to contain non-nil values, without the need
to repeat non-generic guard-let structures with the same else code-block.

Best regards,

Jack

_______________________________________________
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


(Jack Newcombe) #9

This can actually be accomplished now using a closure:

  let value = optionalValue ?? { throw CustomError.failure }()

However, this adds a layer of indirection that I’m not keen on and lacks the readability and maintainability of a well-defined operator.

The problem with changing the nil-coalescing operator is that it means allowing the second operand to be a statement rather than an expression, which I assume would be seen as an unacceptable.

···

On 9 Feb 2017, at 07:56, Brent Royal-Gordon <brent@architechies.com> wrote:

On Feb 8, 2017, at 12:00 PM, Jack Newcombe via swift-evolution <swift-evolution@swift.org> wrote:

I propose the introduction of a nil-rejection operator (represented here as !!) as a complement to the above operators.
.
This operator should allow an equivalent behaviour to the forced unwrapping of a variable, but with the provision of an error to throw in place of throwing a fatal error.

- value !! Error :
  if value is nil, throw non-fatal error
  if value is not nil, return value

Example of how this syntax might work (Where CustomError: Error):

  let value = try optionalValue !! CustomError.failure

Rather than invent a new operator, I'd prefer to make `throw` an expression rather than a statement. Then you could write:

  let value = optionalValue ?? throw CustomError.Failure

One issue here would be figuring out the proper return type for `throw`. Although if `Never` were a subtype-of-all-types, that would of course work. :^)

--
Brent Royal-Gordon
Architechies


(Ben Cohen) #10

It doesn’t seem from reviewing the thread that there was much consensus regarding whether this is something that should be added or how it would be spelled, so probably needs more pitching before making it to a proposal.

My own personal preference: Since ! is so strongly associated with force-unwrapping, using !! for throwing not trapping doesn’t seem appropriate.

I would rather see !! used for something slightly different, which is force-unwrap with an explanation. e.g.

  someOptional !! “this should never be nil for reasons”

would be equivalent to the expression:

  someOptional != nil ? someOptional! : fatalError(“this should never be nil for reasons”) // not that this compiles but you get the idea

Justification being: force unwrap isn’t universally bad despite what you might hear sometimes, and can be read as a precondition that nil is impossible/unhandleable at this point in the code. But preconditions should usually be accompanied with an explanation to output when they fail, which postfix ! doesn’t allow for but infix !! would.

···

On Feb 20, 2017, at 10:22 AM, Jack Newcombe via swift-evolution <swift-evolution@swift.org> wrote:

Hi all,

Now that phase 2 has begun, am I able to submit a proposal for this?

Best regards,

Jack

On 8 Feb 2017, at 20:00, Jack Newcombe <jack@newcombe.io <mailto:jack@newcombe.io>> wrote:

Hi all,

Currently there are a number of different operators for dealing with optionals that cover most of the use cases. However, I think I’ve identified a missing complement for the existing operators for optionals.

Take the following outcomes for interacting with an optional using existing operators (!, ?, ??). The outcomes of using these are as follows:

- value? :
  if value is nil, do nothing and return nil
  if value is not nil, complete the chain by evaluating the rest of the expression. Return the result of the expression
- value! :
  if value is nil, throw.a fatal error.
  If value is not nil, complete the chain by evaluating the rest of the expression. Return the result of the expression
- value ?? default :
  if value is nil, return default
  if value is not nil, return value

It seems to me that, if it is possible to coalesce a nil value with a default value, it should also be possible to reject a nil value a non-fatal error.

I propose the introduction of a nil-rejection operator (represented here as !!) as a complement to the above operators.
.
This operator should allow an equivalent behaviour to the forced unwrapping of a variable, but with the provision of an error to throw in place of throwing a fatal error.

- value !! Error :
  if value is nil, throw non-fatal error
  if value is not nil, return value

Example of how this syntax might work (Where CustomError: Error):

  let value = try optionalValue !! CustomError.failure

It is possible to implement this in Swift 3 with the following declaration:

  infix operator !! : NilCoalescingPrecedence

  func !!<UnwrappedType: Any, ErrorType: Error>(lhs: Optional<UnwrappedType>, rhs: ErrorType) throws -> UnwrappedType {
      guard let unwrappedValue = lhs else {
          throw rhs
      }
      return unwrappedValue
  }

I’ve added further examples including composition with the nil-coalescence operator here:
https://gist.github.com/jnewc/304bdd2d330131ddb8a1e615ee560d1d

This would be particularly convenient in cases where a functions expects significant number of optional to contain non-nil values, without the need to repeat non-generic guard-let structures with the same else code-block.

Best regards,

Jack

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


(Chris Lattner) #11

Hi all,

Now that phase 2 has begun, am I able to submit a proposal for this?

Hi Jack,

Stage 2 has limited scope, proposals are on topic if they fit the criteria Ted laid out in his email:
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170213/032116.html

-Chris

···

On Feb 20, 2017, at 10:22 AM, Jack Newcombe via swift-evolution <swift-evolution@swift.org> wrote:

Best regards,

Jack

On 8 Feb 2017, at 20:00, Jack Newcombe <jack@newcombe.io <mailto:jack@newcombe.io>> wrote:

Hi all,

Currently there are a number of different operators for dealing with optionals that cover most of the use cases. However, I think I’ve identified a missing complement for the existing operators for optionals.

Take the following outcomes for interacting with an optional using existing operators (!, ?, ??). The outcomes of using these are as follows:

- value? :
  if value is nil, do nothing and return nil
  if value is not nil, complete the chain by evaluating the rest of the expression. Return the result of the expression
- value! :
  if value is nil, throw.a fatal error.
  If value is not nil, complete the chain by evaluating the rest of the expression. Return the result of the expression
- value ?? default :
  if value is nil, return default
  if value is not nil, return value

It seems to me that, if it is possible to coalesce a nil value with a default value, it should also be possible to reject a nil value a non-fatal error.

I propose the introduction of a nil-rejection operator (represented here as !!) as a complement to the above operators.
.
This operator should allow an equivalent behaviour to the forced unwrapping of a variable, but with the provision of an error to throw in place of throwing a fatal error.

- value !! Error :
  if value is nil, throw non-fatal error
  if value is not nil, return value

Example of how this syntax might work (Where CustomError: Error):

  let value = try optionalValue !! CustomError.failure

It is possible to implement this in Swift 3 with the following declaration:

  infix operator !! : NilCoalescingPrecedence

  func !!<UnwrappedType: Any, ErrorType: Error>(lhs: Optional<UnwrappedType>, rhs: ErrorType) throws -> UnwrappedType {
      guard let unwrappedValue = lhs else {
          throw rhs
      }
      return unwrappedValue
  }

I’ve added further examples including composition with the nil-coalescence operator here:
https://gist.github.com/jnewc/304bdd2d330131ddb8a1e615ee560d1d

This would be particularly convenient in cases where a functions expects significant number of optional to contain non-nil values, without the need to repeat non-generic guard-let structures with the same else code-block.

Best regards,

Jack

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


(Hooman Mehr) #12

I think the best solution is overloading the existing ?? operator. It is very easy to do:

func ??<T,U: Error>(lhs: T?, rhs: U) throws -> T {
    
    if let lhs = lhs { return lhs } else { throw rhs }
}

then you can say:

do {
    
    let y = try x ?? myError
    
} catch ...

It might even make sense to add to the standard library.

···

On Feb 9, 2017, at 12:04 AM, Jack Newcombe via swift-evolution <swift-evolution@swift.org> wrote:

This can actually be accomplished now using a closure:

  let value = optionalValue ?? { throw CustomError.failure }()

However, this adds a layer of indirection that I’m not keen on and lacks the readability and maintainability of a well-defined operator.

The problem with changing the nil-coalescing operator is that it means allowing the second operand to be a statement rather than an expression, which I assume would be seen as an unacceptable.

On 9 Feb 2017, at 07:56, Brent Royal-Gordon <brent@architechies.com> wrote:

On Feb 8, 2017, at 12:00 PM, Jack Newcombe via swift-evolution <swift-evolution@swift.org> wrote:

I propose the introduction of a nil-rejection operator (represented here as !!) as a complement to the above operators.
.
This operator should allow an equivalent behaviour to the forced unwrapping of a variable, but with the provision of an error to throw in place of throwing a fatal error.

- value !! Error :
  if value is nil, throw non-fatal error
  if value is not nil, return value

Example of how this syntax might work (Where CustomError: Error):

  let value = try optionalValue !! CustomError.failure

Rather than invent a new operator, I'd prefer to make `throw` an expression rather than a statement. Then you could write:

  let value = optionalValue ?? throw CustomError.Failure

One issue here would be figuring out the proper return type for `throw`. Although if `Never` were a subtype-of-all-types, that would of course work. :^)

--
Brent Royal-Gordon
Architechies

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


(Jack Newcombe) #13

While this is nice, it adds ambiguity to the nil-coalescing operator. For
example, when using nil-coalescing with a wrapped error value and an
unwrapped error value as operands:

    let optionalError: Errors? = nil
    let result = optionalError ?? Errors.generic

The above will result in an "Ambiguous use of operator" error. Even if you
were to somehow constrain the first argument to arguments of non-error
types, it would still make the operator incongruous.

···

On Thu, Feb 9, 2017 at 7:08 PM, Hooman Mehr <hooman@mac.com> wrote:

I think the best solution is overloading the existing ?? operator. It is
very easy to do:

func ??<T,U: Error>(lhs: T?, rhs: U) throws -> T {

    if let lhs = lhs { return lhs } else { throw rhs }
}

then you can say:

do {

    let y = try x ?? myError

} catch ...

It might even make sense to add to the standard library.

On Feb 9, 2017, at 12:04 AM, Jack Newcombe via swift-evolution < > swift-evolution@swift.org> wrote:

This can actually be accomplished now using a closure:

let value = optionalValue ?? { throw CustomError.failure }()

However, this adds a layer of indirection that I’m not keen on and lacks
the readability and maintainability of a well-defined operator.

The problem with changing the nil-coalescing operator is that it means
allowing the second operand to be a statement rather than an expression,
which I assume would be seen as an unacceptable.

On 9 Feb 2017, at 07:56, Brent Royal-Gordon <brent@architechies.com> > wrote:

On Feb 8, 2017, at 12:00 PM, Jack Newcombe via swift-evolution < > swift-evolution@swift.org> wrote:

I propose the introduction of a nil-rejection operator (represented here
as !!) as a complement to the above operators.
.
This operator should allow an equivalent behaviour to the forced
unwrapping of a variable, but with the provision of an error to throw in
place of throwing a fatal error.

- value !! Error :
if value is nil, throw non-fatal error
if value is not nil, return value

Example of how this syntax might work (Where CustomError: Error):

let value = try optionalValue !! CustomError.failure

Rather than invent a new operator, I'd prefer to make `throw` an
expression rather than a statement. Then you could write:

let value = optionalValue ?? throw CustomError.Failure

One issue here would be figuring out the proper return type for `throw`.
Although if `Never` were a subtype-of-all-types, that would of course work.
:^)

--
Brent Royal-Gordon
Architechies

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


(Haravikk) #14

Interesting idea, but what if you actually want that error assigned to y?

I wonder if an alternative to the original proposal might be to allow throw on the right hand side? So you could do:

  let y = x ?? throw myError

That'd be a lot more explicit about what's going on than a new operator would be. It might require defining throw as being some kind of special type though that can be used to satisfy only function signatures that call for it explicitly like:

  func ??<T,U:_Throw>(lhs:T?, rhs:U) throws -> T {
    guard let lhs = lhs else { throw rhs.error }
    return lhs
  }

Same idea really, but the difference being that this wouldn't be a type that satisfies generics, but can be used for any operator that could allow a throw to occur within its operands.

···

On 9 Feb 2017, at 19:08, Hooman Mehr via swift-evolution <swift-evolution@swift.org> wrote:

I think the best solution is overloading the existing ?? operator. It is very easy to do:

func ??<T,U: Error>(lhs: T?, rhs: U) throws -> T {
    
    if let lhs = lhs { return lhs } else { throw rhs }
}

then you can say:

do {
    
    let y = try x ?? myError
    
} catch ...

It might even make sense to add to the standard library.


(Hooman Mehr) #15

Two more tiny overloads is all takes to fix it:

func ??<T: Error>(lhs: T?, rhs: T) -> T {
    
    if let lhs = lhs { return lhs } else { return rhs }
}

func ??<T: Error, U: Error>(lhs: T?, rhs: U) -> Error {
    
    if let lhs = lhs { return lhs } else { return rhs }
}

···

On Feb 9, 2017, at 12:01 PM, Jack Newcombe <jack@newcombe.io> wrote:

While this is nice, it adds ambiguity to the nil-coalescing operator. For example, when using nil-coalescing with a wrapped error value and an unwrapped error value as operands:

    let optionalError: Errors? = nil
    let result = optionalError ?? Errors.generic

The above will result in an "Ambiguous use of operator" error. Even if you were to somehow constrain the first argument to arguments of non-error types, it would still make the operator incongruous.

On Thu, Feb 9, 2017 at 7:08 PM, Hooman Mehr <hooman@mac.com <mailto:hooman@mac.com>> wrote:
I think the best solution is overloading the existing ?? operator. It is very easy to do:

func ??<T,U: Error>(lhs: T?, rhs: U) throws -> T {
    
    if let lhs = lhs { return lhs } else { throw rhs }
}

then you can say:

do {
    
    let y = try x ?? myError
    
} catch ...

It might even make sense to add to the standard library.

On Feb 9, 2017, at 12:04 AM, Jack Newcombe via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This can actually be accomplished now using a closure:

  let value = optionalValue ?? { throw CustomError.failure }()

However, this adds a layer of indirection that I’m not keen on and lacks the readability and maintainability of a well-defined operator.

The problem with changing the nil-coalescing operator is that it means allowing the second operand to be a statement rather than an expression, which I assume would be seen as an unacceptable.

On 9 Feb 2017, at 07:56, Brent Royal-Gordon <brent@architechies.com <mailto:brent@architechies.com>> wrote:

On Feb 8, 2017, at 12:00 PM, Jack Newcombe via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I propose the introduction of a nil-rejection operator (represented here as !!) as a complement to the above operators.
.
This operator should allow an equivalent behaviour to the forced unwrapping of a variable, but with the provision of an error to throw in place of throwing a fatal error.

- value !! Error :
  if value is nil, throw non-fatal error
  if value is not nil, return value

Example of how this syntax might work (Where CustomError: Error):

  let value = try optionalValue !! CustomError.failure

Rather than invent a new operator, I'd prefer to make `throw` an expression rather than a statement. Then you could write:

  let value = optionalValue ?? throw CustomError.Failure

One issue here would be figuring out the proper return type for `throw`. Although if `Never` were a subtype-of-all-types, that would of course work. :^)

--
Brent Royal-Gordon
Architechies

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


(Rob Mayoff) #16

You can do this today:

extension Error {
    func throwMe<R>() throws -> R { throw self }
}

let y = try x ?? MyError().throwMe()

···

On Thu, Feb 9, 2017 at 2:25 PM, Haravikk via swift-evolution < swift-evolution@swift.org> wrote:

I wonder if an alternative to the original proposal might be to allow
throw on the right hand side? So you could do:

let y = x ?? throw myError


(Jon Shier) #17

I created this set of extensions so I could more easily deal with optionals in a context where I was already potentially throwing errors.

extension Optional {
    
    func deoptionalize() throws -> Wrapped {
        switch self {
        case .some(let wrapped): return wrapped
        case .none: throw "\(self.self) was nil."
        }
    }
    
}

extension String: LocalizedError {
    
    public var localizedDescription: String {
        return self
    }
    
}

It’s probably a bad idea to make String an Error, but it worked. It seems to me like there should be something more convenient for dealing with Optionals in contexts where unwrapping them is awkward.

Jon

···

On Feb 9, 2017, at 5:35 PM, Rob Mayoff via swift-evolution <swift-evolution@swift.org> wrote:

On Thu, Feb 9, 2017 at 2:25 PM, Haravikk via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I wonder if an alternative to the original proposal might be to allow throw on the right hand side? So you could do:

  let y = x ?? throw myError

You can do this today:

extension Error {
    func throwMe<R>() throws -> R { throw self }
}

let y = try x ?? MyError().throwMe()

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


(John McCall) #18

I created this set of extensions so I could more easily deal with optionals in a context where I was already potentially throwing errors.

extension Optional {
    
    func deoptionalize() throws -> Wrapped {
        switch self {
        case .some(let wrapped): return wrapped
        case .none: throw "\(self.self) was nil."
        }
    }
    
}

extension String: LocalizedError {
    
    public var localizedDescription: String {
        return self
    }
    
}

It’s probably a bad idea to make String an Error, but it worked.

Yeah, this seems reasonable (although you might consider making some earlier part throw the error instead of returning an optional?) except for the completely unnecessary use of String as the error type. Just make a new error enum with one case; it'll be much easier to recognize it if you need to.

Also, I'm pretty sure that that string is always going to expand to "nil was nil."

John.

···

On Feb 16, 2017, at 9:45 PM, Jon Shier via swift-evolution <swift-evolution@swift.org> wrote:

It seems to me like there should be something more convenient for dealing with Optionals in contexts where unwrapping them is awkward.

Jon

On Feb 9, 2017, at 5:35 PM, Rob Mayoff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Thu, Feb 9, 2017 at 2:25 PM, Haravikk via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I wonder if an alternative to the original proposal might be to allow throw on the right hand side? So you could do:

  let y = x ?? throw myError

You can do this today:

extension Error {
    func throwMe<R>() throws -> R { throw self }
}

let y = try x ?? MyError().throwMe()

_______________________________________________
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