!? operator for ternary conditional unwrapping


(Maxim Veksler) #1

Hello,

Let's assume I have an optional "name" parameter, based on the optional
status of the parameters I would like to compose string with either the
unwrapped value of name, or the string "null". The use case for is
syntactic sugar to compose a GraphQL queries.

A (sampled) illustration of the code that currently solves it looks as
following:

func query(name: String?) {
  let variables_name = name != nil ? "\"\(name!)\"" : "null"
return "{ param: \(variables_name) }"
}

Based on optional status the following output is expected

let name = "Max"
query(name: name)
// { param: "Max" }

let name: String? = nil
query(name: name)
// { param: null }

I think it might be useful to have an conditional unwrap operator !? that
would enable syntax sugar uch as the following built into the language:

func query(name: String?) {
  return "{ param: \(name !? "\"\(name)\"": "null") }"
}

This expression is expected to produce same output as the examples above.
It means check the Optional state of name: String?, in case it has a value,
unwrap it and make the unwrapped value accessible under the same name to
the true condition part of the expression, otherwise return the false condition
part of the expression.

The effectively removes the need to have the "if != nil" and the forced
unwrap syntactical code, and IMHO improves code readability and
expressiveness.

I'm wondering what the community thinks of the displayed use case, and
proposed solution?

-m


(Haravikk) #2

I'm a bit undecided, as it seems like it doesn't add enough to warrant another operator to learn (and one that's a bit confusing in its purpose, since it doesn't trap like other exclamation mark operators do).

For the specific example you can already just do:

  func query(name:String?) -> String { return "{ param: \"" + (name ?? "null") + "\" }" }

In the case where the input value isn't an optional string you can still do:

  func myFunc(foo:Foo?) -> String { return foo?.description ?? "null" }

I dunno, I'm just not sure having another ternary adds quite enough to justify it, and like I say it's a bit of an odd operator since it doesn't actually trap. For the specific case of a String a more useful alternative might be the ability to put the ?? operator inside a string, with the compiler knowing the final type must be a String (so you can mix types as long as they're CustomStringConvertible), like so:

  func query(name:String?) -> String { return "{ param: \"\(name ?? "null")\"" }

···

On 8 Feb 2017, at 14:04, Maxim Veksler via swift-evolution <swift-evolution@swift.org> wrote:

Hello,

Let's assume I have an optional "name" parameter, based on the optional status of the parameters I would like to compose string with either the unwrapped value of name, or the string "null". The use case for is syntactic sugar to compose a GraphQL queries.

A (sampled) illustration of the code that currently solves it looks as following:

func query(name: String?) {
  let variables_name = name != nil ? "\"\(name!)\"" : "null"
  return "{ param: \(variables_name) }"
}

Based on optional status the following output is expected

let name = "Max"
query(name: name)
// { param: "Max" }

let name: String? = nil
query(name: name)
// { param: null }

I think it might be useful to have an conditional unwrap operator !? that would enable syntax sugar uch as the following built into the language:

func query(name: String?) {
  return "{ param: \(name !? "\"\(name)\"": "null") }"
}

This expression is expected to produce same output as the examples above. It means check the Optional state of name: String?, in case it has a value, unwrap it and make the unwrapped value accessible under the same name to the true condition part of the expression, otherwise return the false condition part of the expression.

The effectively removes the need to have the "if != nil" and the forced unwrap syntactical code, and IMHO improves code readability and expressiveness.

I'm wondering what the community thinks of the displayed use case, and proposed solution?


(Tony Allevato) #3

What you're asking for is already possible (avoiding the optional unwrap)
by combining map() on Optional with ??:

let name1: String? = "name"
print(name1.map { "\"\($0)\"" } ?? "null")  // prints "\"name\""

let name2: String? = nil
print(name2.map { "\"\($0)\"" } ?? "null")  // prints "null"

So I guess the question is, does simplifying that rise to the level of
wanting a custom operator? I personally don't think it does, but I could
see an argument being made that an operator with defined semantics might be
a small amount clearer than map + ??. But I think the benefits would have
to be very strong, though.

And as other folks have mentioned, having "!" in the operator name is
somewhat misleading, since when I see that I expect a trap to occur in nil
cases.

···

On Wed, Feb 8, 2017 at 6:04 AM Maxim Veksler via swift-evolution < swift-evolution@swift.org> wrote:

Hello,

Let's assume I have an optional "name" parameter, based on the optional
status of the parameters I would like to compose string with either the
unwrapped value of name, or the string "null". The use case for is
syntactic sugar to compose a GraphQL queries.

A (sampled) illustration of the code that currently solves it looks as
following:

func query(name: String?) {
  let variables_name = name != nil ? "\"\(name!)\"" : "null"
return "{ param: \(variables_name) }"
}

Based on optional status the following output is expected

let name = "Max"
query(name: name)
// { param: "Max" }

let name: String? = nil
query(name: name)
// { param: null }

I think it might be useful to have an conditional unwrap operator !? that
would enable syntax sugar uch as the following built into the language:

func query(name: String?) {
  return "{ param: \(name !? "\"\(name)\"": "null") }"
}

This expression is expected to produce same output as the examples above.
It means check the Optional state of name: String?, in case it has a
value, unwrap it and make the unwrapped value accessible under the same
name to the true condition part of the expression, otherwise return the false
condition part of the expression.

The effectively removes the need to have the "if != nil" and the forced
unwrap syntactical code, and IMHO improves code readability and
expressiveness.

I'm wondering what the community thinks of the displayed use case, and
proposed solution?

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


(Martin R) #4

Isn't that exactly what the nil-coalescing operator ?? does?

    func query(name: String?) -> String {
        return "{ param: \(name ?? "null") }"
    }

If you use the JSONSerialization class from the Foundation library then `nil` is bridged to `NSNull` and translated to "null" automatically (see SE-0140).

Regards,
Martin

···

Am 08.02.2017 um 15:04 schrieb Maxim Veksler via swift-evolution <swift-evolution@swift.org>:

Hello,

Let's assume I have an optional "name" parameter, based on the optional status of the parameters I would like to compose string with either the unwrapped value of name, or the string "null". The use case for is syntactic sugar to compose a GraphQL queries.

A (sampled) illustration of the code that currently solves it looks as following:

func query(name: String?) {
  let variables_name = name != nil ? "\"\(name!)\"" : "null"
  return "{ param: \(variables_name) }"
}

Based on optional status the following output is expected

let name = "Max"
query(name: name)
// { param: "Max" }

let name: String? = nil
query(name: name)
// { param: null }

I think it might be useful to have an conditional unwrap operator !? that would enable syntax sugar uch as the following built into the language:

func query(name: String?) {
  return "{ param: \(name !? "\"\(name)\"": "null") }"
}

This expression is expected to produce same output as the examples above. It means check the Optional state of name: String?, in case it has a value, unwrap it and make the unwrapped value accessible under the same name to the true condition part of the expression, otherwise return the false condition part of the expression.

The effectively removes the need to have the "if != nil" and the forced unwrap syntactical code, and IMHO improves code readability and expressiveness.

I'm wondering what the community thinks of the displayed use case, and proposed solution?

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


(Adrian Zubarev) #5

You could always write something like this:

func query(name: String?) -> String {
     
    let variables_name = String(format: name == nil ? "%@" : "\"%@\"", name ?? "null")
    return "{ param: \(variables_name) }"
}

query(name: "Max") //=> "{ param: "Max" }"
query(name: nil) //=> "{ param: null }"
As far as I’m concerned, string interpolation in Swift does not allow nested string literals (yet).

But a ternary operator that could potentially unwrap a list of optional values would be neat.

Bikeshedding:

(optional1, optional2, optional1) ? { tupleWithUnwrappedOptionals in return R } : { R }

// variadic generic parameter list
ternary func ?: <…T, R>(list: (…T?), lhs: ((…T) -> R), rhs: () -> R) -> R {
     
    if let unwrappedList = // unwrap all optionals in `list` {
        return lhs(unwrappedList)
    } else {
        return rhs()
    }
}

···

--
Adrian Zubarev
Sent with Airmail

Am 8. Februar 2017 um 15:04:44, Maxim Veksler via swift-evolution (swift-evolution@swift.org) schrieb:

Hello,

Let's assume I have an optional "name" parameter, based on the optional status of the parameters I would like to compose string with either the unwrapped value of name, or the string "null". The use case for is syntactic sugar to compose a GraphQL queries.

A (sampled) illustration of the code that currently solves it looks as following:

func query(name: String?) {
let
variables_name = name != nil ? "\"\(name!)\"" :
"null"

return "{ param: \(variables_name)
}"

}

Based on optional status the following output is expected

let name = "Max"
query(name: name)
// { param: "Max" }

let name: String? = nil
query(name: name)
// { param: null }

I think it might be useful to have an conditional unwrap operator !? that would enable syntax sugar uch as the following built into the language:

func query(name: String?) {
return
"{ param: \(name !? "\"\(name)\"": "null") }"

}

This expression is
expected to produce same output as the examples above. It means
check the Optional state of name: String?, in case it has a
value, unwrap it and make the unwrapped value accessible under the
same name to the true condition part of the expression, otherwise
return the false
condition part of the expression.

The effectively removes the need to have the
"if != nil" and the forced unwrap syntactical code, and IMHO
improves code readability and
expressiveness.

I'm wondering what the community thinks of the displayed use case, and proposed solution?

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


(Adrian Zubarev) #6

If you need a infix operator that traps, here you have one :wink:

infix operator ?! : NilCoalescingPrecedence

func ?! <T>(optional: T?, noreturn: @autoclosure () -> Never) -> T {
   switch optional {
   case .some(let value):
      return value
   case .none:
      noreturn()
   }
}

let x: Int? = nil

let y: Int = x ?! fatalError("Your message here")

···

--
Adrian Zubarev
Sent with Airmail

Am 8. Februar 2017 um 16:00:24, Haravikk via swift-evolution (swift-evolution@swift.org) schrieb:

I say it's a bit of an odd operator since it doesn't actually trap.


(Adrian Zubarev) #7

I forgot about map :smiley: That solution is more swifty than mine with String(format::).

···

--
Adrian Zubarev
Sent with Airmail

Am 8. Februar 2017 um 16:12:11, Tony Allevato via swift-evolution (swift-evolution@swift.org) schrieb:

What you're asking for is already possible (avoiding the optional unwrap) by combining map() on Optional with ??:

let name1: String? = "name"
print(name1.map { "\"\($0)\"" } ?? "null")  // prints "\"name\""

let name2: String? = nil
print(name2.map { "\"\($0)\"" } ?? "null")  // prints "null"

So I guess the question is, does simplifying that rise to the level of wanting a custom operator? I personally don't think it does, but I could see an argument being made that an operator with defined semantics might be a small amount clearer than map + ??. But I think the benefits would have to be very strong, though.

And as other folks have mentioned, having "!" in the operator name is somewhat misleading, since when I see that I expect a trap to occur in nil cases.

On Wed, Feb 8, 2017 at 6:04 AM Maxim Veksler via swift-evolution <swift-evolution@swift.org> wrote:
Hello,

Let's assume I have an optional "name" parameter, based on the optional status of the parameters I would like to compose string with either the unwrapped value of name, or the string "null". The use case for is syntactic sugar to compose a GraphQL queries.

A (sampled) illustration of the code that currently solves it looks as following:

func query(name: String?) {
let variables_name = name != nil ? "\"\(name!)\""
: "null"
return "{ param: \(variables_name) }"
}

Based on optional status the following output is expected

let name = "Max"
query(name: name)
// { param: "Max" }

let name: String? = nil
query(name: name)
// { param: null }

I think it might be useful to have an conditional unwrap operator !? that would enable syntax sugar uch as the following built into the language:

func query(name: String?) {
return "{ param: \(name !? "\"\(name)\"": "null")
}"
}

This expression is expected to produce same
output as the examples above. It means check the Optional state
of name: String?, in case it has a value, unwrap it and make the
unwrapped value accessible under the same name to the true
condition part of the expression, otherwise return
the false condition part of the expression.

The effectively removes the need to have the "if !=
nil" and the forced unwrap syntactical code, and IMHO improves code
readability and expressiveness.

I'm wondering what the community thinks of the displayed use case, and proposed solution?

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


(Maxim Veksler) #8

Hi Martin, please see reply inline.

Isn't that exactly what the nil-coalescing operator ?? does?

    func query(name: String?) -> String {
        return "{ param: \(name ?? "null") }"
    }

No, because

func query(name: String?) -> String {

    return "{ param: \(name ?? "null") }"

}

print(query(name: nil)) //=> { param: null }

print(query(name: "Max")) //=> { param: Max }

The output I'm expecting from the second call should be is { param: "Max" }

If you use the JSONSerialization class from the Foundation library then
`nil` is bridged to `NSNull` and translated to "null" automatically (see
SE-0140).

I agree that in this case JSONSerialization might be a fitting solution, I
also agree that any required output can be solved by a Utility class.

What I would like to argue is that there is a place for such syntactic
sugar because it simplifies readability, for ex. what if I've want to wrap
my non optional name parameter using %20%, and co.

JSONSerialization is specific solution, I think that there is a generic
case to have in the language some syntactic sugar for a 1 liner composition
of unwrap and ternary conditional operation.

Regards,

···

On Wed, Feb 8, 2017 at 4:47 PM Martin R <martinr448@gmail.com> wrote:

Martin

> Am 08.02.2017 um 15:04 schrieb Maxim Veksler via swift-evolution < > swift-evolution@swift.org>:
>
> Hello,
>
> Let's assume I have an optional "name" parameter, based on the optional
status of the parameters I would like to compose string with either the
unwrapped value of name, or the string "null". The use case for is
syntactic sugar to compose a GraphQL queries.
>
> A (sampled) illustration of the code that currently solves it looks as
following:
>
> func query(name: String?) {
> let variables_name = name != nil ? "\"\(name!)\"" : "null"
> return "{ param: \(variables_name) }"
> }
>
> Based on optional status the following output is expected
>
> let name = "Max"
> query(name: name)
> // { param: "Max" }
>
> let name: String? = nil
> query(name: name)
> // { param: null }
>
> I think it might be useful to have an conditional unwrap operator !?
that would enable syntax sugar uch as the following built into the language:
>
> func query(name: String?) {
> return "{ param: \(name !? "\"\(name)\"": "null") }"
> }
>
> This expression is expected to produce same output as the examples
above. It means check the Optional state of name: String?, in case it has a
value, unwrap it and make the unwrapped value accessible under the same
name to the true condition part of the expression, otherwise return the
false condition part of the expression.
>
> The effectively removes the need to have the "if != nil" and the forced
unwrap syntactical code, and IMHO improves code readability and
expressiveness.
>
> I'm wondering what the community thinks of the displayed use case, and
proposed solution?
>
>
> -m
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution


(Maxim Veksler) #9

Hello Adrian,

Please see reply inline.

You could always write something like this:

func query(name: String?) -> String {

    let variables_name = String(format: name == nil ? "%@" : "\"%@\"", name ?? "null")
    return "{ param: \(variables_name) }"
}

query(name: "Max") //=> "{ param: "Max" }"
query(name: nil) //=> "{ param: null }"

As far as I’m concerned, string interpolation in Swift does not allow
nested string literals (yet).

Thanks, that's another take on the specifics of String, but I think *?!*
extends beyond String.

For example, assume that we're dealing with a callback of some form.

let callback: CallbackProtocol? = StructExampleCallback()

You could write inside you handler function:

func handler() -> Result? {
  return callback ?! callback.result() : nil
}

Which would return either nil, or the callback result. I don't think you
can do it with ?? but then again guard is a valid solution for this
specific use case.

···

On Wed, Feb 8, 2017 at 4:52 PM Adrian Zubarev < adrian.zubarev@devandartist.com> wrote:

------------------------------

But a ternary operator that could potentially unwrap a list of optional
values would be neat.

Bikeshedding:

(optional1, optional2, optional1) ? { tupleWithUnwrappedOptionals in return R } : { R }

// variadic generic parameter list
ternary func ?: <…T, R>(list: (…T?), lhs: ((…T) -> R), rhs: () -> R) -> R {

    if let unwrappedList = // unwrap all optionals in `list` {
        return lhs(unwrappedList)
    } else {
        return rhs()
    }
}

--
Adrian Zubarev
Sent with Airmail

Am 8. Februar 2017 um 15:04:44, Maxim Veksler via swift-evolution (
swift-evolution@swift.org) schrieb:

Hello,

Let's assume I have an optional "name" parameter, based on the optional
status of the parameters I would like to compose string with either the
unwrapped value of name, or the string "null". The use case for is
syntactic sugar to compose a GraphQL queries.

A (sampled) illustration of the code that currently solves it looks as
following:

func query(name: String?) {
  let variables_name = name != nil ? "\"\(name!)\"" : "null"
return "{ param: \(variables_name) }"
}

Based on optional status the following output is expected

let name = "Max"
query(name: name)
// { param: "Max" }

let name: String? = nil
query(name: name)
// { param: null }

I think it might be useful to have an conditional unwrap operator !? that
would enable syntax sugar uch as the following built into the language:

func query(name: String?) {
  return "{ param: \(name !? "\"\(name)\"": "null") }"
}

This expression is expected to produce same output as the examples above.
It means check the Optional state of name: String?, in case it has a
value, unwrap it and make the unwrapped value accessible under the same
name to the true condition part of the expression, otherwise return the
false condition part of the expression.

The effectively removes the need to have the "if != nil" and the forced
unwrap syntactical code, and IMHO improves code readability and
expressiveness.

I'm wondering what the community thinks of the displayed use case, and
proposed solution?

-m

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


(Maxim Veksler) #10

Thanks for teaching me about .map on Optional. Very cool.

I agree with the comment of $! misleading, maybe a better alternative would
be "??" ?

···

On Wed, Feb 8, 2017 at 5:12 PM Tony Allevato <tony.allevato@gmail.com> wrote:

What you're asking for is already possible (avoiding the optional unwrap)
by combining map() on Optional with ??:

let name1: String? = "name"
print(name1.map { "\"\($0)\"" } ?? "null")  // prints "\"name\""

let name2: String? = nil
print(name2.map { "\"\($0)\"" } ?? "null")  // prints "null"

So I guess the question is, does simplifying that rise to the level of
wanting a custom operator? I personally don't think it does, but I could
see an argument being made that an operator with defined semantics might be
a small amount clearer than map + ??. But I think the benefits would have
to be very strong, though.

And as other folks have mentioned, having "!" in the operator name is
somewhat misleading, since when I see that I expect a trap to occur in nil
cases.

On Wed, Feb 8, 2017 at 6:04 AM Maxim Veksler via swift-evolution < > swift-evolution@swift.org> wrote:

Hello,

Let's assume I have an optional "name" parameter, based on the optional
status of the parameters I would like to compose string with either the
unwrapped value of name, or the string "null". The use case for is
syntactic sugar to compose a GraphQL queries.

A (sampled) illustration of the code that currently solves it looks as
following:

func query(name: String?) {
  let variables_name = name != nil ? "\"\(name!)\"" : "null"
return "{ param: \(variables_name) }"
}

Based on optional status the following output is expected

let name = "Max"
query(name: name)
// { param: "Max" }

let name: String? = nil
query(name: name)
// { param: null }

I think it might be useful to have an conditional unwrap operator !? that
would enable syntax sugar uch as the following built into the language:

func query(name: String?) {
  return "{ param: \(name !? "\"\(name)\"": "null") }"
}

This expression is expected to produce same output as the examples above.
It means check the Optional state of name: String?, in case it has a
value, unwrap it and make the unwrapped value accessible under the same
name to the true condition part of the expression, otherwise return the false
condition part of the expression.

The effectively removes the need to have the "if != nil" and the forced
unwrap syntactical code, and IMHO improves code readability and
expressiveness.

I'm wondering what the community thinks of the displayed use case, and
proposed solution?

-m

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


(Anton Zhilin) #11

With Runes <https://github.com/thoughtbot/Runes>, this looks like:

(name1 <^> { "\"\($0)\"" }) ?? "null"

Runes defines <^> to have lower precedence than ??. Sadly, they all can’t
be in the same precedence group due to right associativity of ??.

What you're asking for is already possible (avoiding the optional unwrap)

by combining map() on Optional with ??:

let name1: String? = "name"
print(name1.map { "\"\($0)\"" } ?? "null")  // prints "\"name\""

let name2: String? = nil
print(name2.map { "\"\($0)\"" } ?? "null")  // prints "null"

So I guess the question is, does simplifying that rise to the level of
wanting a custom operator? I personally don't think it does, but I could
see an argument being made that an operator with defined semantics might be
a small amount clearer than map + ??. But I think the benefits would have
to be very strong, though.

And as other folks have mentioned, having "!" in the operator name is
somewhat misleading, since when I see that I expect a trap to occur in nil
cases.

···

2017-02-08 18:11 GMT+03:00 Tony Allevato via swift-evolution < swift-evolution@swift.org>:


(Adrian Zubarev) #12

Swift does already have a great solution in such a scenario.

func handler() -> Result? {
  return callback?.result()
}
Does the trick. :wink:

···

--
Adrian Zubarev
Sent with Airmail

Am 8. Februar 2017 um 20:02:38, Maxim Veksler (maxim@vekslers.org) schrieb:

For example, assume that we're dealing with a callback of some form.

let callback: CallbackProtocol? = StructExampleCallback()

You could write inside you handler function:

func handler() -> Result? {
return callback ?! callback.result() : nil
}

Which would return either nil, or the callback result. I don't think you can do it with ?? but then again guard is a valid solution for this specific use case.


(Adrian Zubarev) #13

For more information about optional chaining read this docs: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html

···

--
Adrian Zubarev
Sent with Airmail

Am 8. Februar 2017 um 20:05:31, Adrian Zubarev (adrian.zubarev@devandartist.com) schrieb:

Swift does already have a great solution in such a scenario.

func handler() -> Result? {
  return callback?.result()
}
Does the trick. :wink:

--
Adrian Zubarev
Sent with Airmail

Am 8. Februar 2017 um 20:02:38, Maxim Veksler (maxim@vekslers.org) schrieb:

For example, assume that we're dealing with a callback of some form.

let callback: CallbackProtocol? = StructExampleCallback()

You could write inside you handler function:

func handler() -> Result? {
return callback ?! callback.result() : nil
}

Which would return either nil, or the callback result. I don't think you can do it with ?? but then again guard is a valid solution for this specific use case.