[Proposal draft] Disallow Optionals in String Interpolation Segments

If you don't think this needs a proposal, then Robert has an implementation almost done. We could submit a PR later today.

- Harlan

···

On Oct 3, 2016, at 4:06 PM, Mark Lacey via swift-evolution <swift-evolution@swift.org> wrote:

On Oct 3, 2016, at 11:26 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Oct 3, 2016, at 11:02 AM, Robert Widmann <devteam.codafi@gmail.com> wrote:

Because the initializer here doesn’t take Any, it takes <T>.

I think there's a case to be made to generalize the 'Any' warning to Optional implicitly being deduced as a type variable binding in any unconstrained context. What exactly constitutes 'implicit' and 'unconstrained' is up for debate, though, and probably needs some experimentation to figure out what feels good. For instance, explicitly constructing an optional is a signal the optionality intentional. Potentially, having multiple Optional parameters binding the same type variable also increases the likelihood it's intended, for example:

func foo<T>(x: T, y: T) {}

var x: Int? = 1
var y: Int = 2
foo(x, y) // One Optional isn't unwrapped, forcing the other to promote. Maybe a mistake?
var z: Int? = 3
foo(x, z) // Two T parameters are Optional. Probably intentional?

Regardless of whether there's a more general principle we can base a warning on, string interpolation and String(describing:) are common enough pitfalls that they may just deserve special case treatment.

I think string interpolation could be handled pretty easily with a warning by extending the existing warning for Any. We just need to look at interpolation expressions and if any of the segments are optional-typed emit a warning unless they are explicitly casted to the optional type. The fixit can suggest explicit casting or using the debugDescription.

I’m not sure we really need an evolution proposal for that.

As for the more general topic of trickiness around optional injection into unconstrained generics: Yes, we should review that at some point as well. I recall seeing at least one concrete complaint about surprising behavior resulting from doing this in generic functions, but I cannot find the bug at the moment.

Mark

-Joe

~Robert Widmann

On Oct 3, 2016, at 2:00 PM, Harlan Haskins via swift-evolution <swift-evolution@swift.org> wrote:

Unfortunately, Optional-to-Any does not currently hit this case because IIRC it doesn't promote to Any in an interpolation segment. I tested this with a ToT build yesterday.

- Harlan

On Oct 3, 2016, at 1:57 PM, Joe Groff <jgroff@apple.com> wrote:

We now emit a warning whenever an optional is used as an Any. I disagree that this should be an error, but it seems reasonable to warn (if we don't already thanks to the 'Any' warning).

-Joe

On Oct 3, 2016, at 10:52 AM, Harlan Haskins via swift-evolution <swift-evolution@swift.org> wrote:

Hey all,

Julio Carrettoni, Robert Widmann, and I have been working on a proposal to mitigate something that's burned us all since Swift 1. We'd love some feedback!

It's available here: Disallow Optionals in String Interpolation · GitHub

I've posted the current draft below.

Thanks,
Harlan Haskins

Disallow Optionals in String Interpolation Segments

Proposal: SE-NNNN
Authors: Harlan Haskins, Julio Carrettoni, Robert Widmann
Review Manager: TBD
Status: Awaiting revie
Introduction

Swift developers frequently use string interpolation as a convenient, concise syntax for interweaving variable values with strings. The interpolation machinery, however, has surprising behavior in one specific case: Optional<T>. If a user puts an optional value into a string interpolation segment, it will insert either "Optional("value")" or "nil" in the resulting string. Neither of these is particularly desirable, so we propose a warning and fix-it to surface solutions to these potential mistakes.

Swift-evolution thread: Discussion thread topic for that proposal

Motivation

The Swift Programming Language defines string interpolation segments as "a way to construct a new String value from a mix of constants, variables, literals, and expressions". There is one type that runs counter to this definition: Optional. The .none case in particular is used to indicate the absence of a value. Moreover, its inclusion in interpolation segments leads to the dreaded "nil" in output that is often fed to UI elements. Even barring that, interpolating a non-nil optional value yields "Optional("value")", a result that is not useful even in logged output.

Given that the Optional type is never fit for display to the end user, and can often be a surprising find in the console, we propose that requesting an Optional's debug description be an explicit act. This proposal now requires a warning when using an expression of Optional type within a string interpolation segment.

Proposed solution

The user will be warned after attempting to use an expression with type Optional<T> in a string interpolation segment. They will then be offered a fixit suggesting they explicitly request the debugDescription of the Optional value instead.

Detailed design

Semantic analysis currently does not do much but guarantee the well-formedness of expressions in interpolation segments. These are then fed directly to String.init(stringInterpolationSegment:) and are run through the runtime reflection system to generate a description. Semantic analysis will be tweaked to inspect the result of solving an interpolation segment for an Optional and will offer a fixit in that case.

Impact on existing code

As this is a warning, code written before this proposal will continue to compile and run with the same semantics as before. Authors of code that makes use of this unsafe pattern will be offered a migration path to the safer, more explicit form.

Alternatives considered

A fixit that suggests a default value be inserted would be entirely appropriate (following the style of the fixit introduced in SE-0140).

Forbidding this pattern by hard error would make this proposal a breaking change that is out of scope for this stage of Swift's development.

A fixit that introduces a force-unwrapping would technically work as well, however it would be fixing a dangerous operation with yet another dangerous operation.

Sent from my iPad
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

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

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

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

I would say it's surprising if you don't expect the value to be optional. Swift is such that you can write very long programs without knowing yourself the static type of every variable. It just takes one Optional property of a non-optional struct passed into a string interpolation segment to cause this behavior, as Keith showed earlier with url.absoluteString. This is especially bad because APIs are being notated with optionality, like in the Swift 2.2 -> 2.3 shift, that were previously implicitly unwrapped.

I think a warning is the best way to go here. A developer probably didn't intend to interpolate an optional (I've never personally wanted the behavior) and this lets them know.

- Harlan

···

On Oct 4, 2016, at 8:31 AM, Jeremy Pereira <jeremy.j.pereira@googlemail.com> wrote:

On 3 Oct 2016, at 22:41, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

On Mon, Oct 3, 2016, at 10:52 AM, Harlan Haskins via swift-evolution wrote:

Swift developers frequently use string interpolation as a convenient, concise syntax for interweaving variable values with strings. The interpolation machinery, however, has surprising behavior in one specific case: Optional<T>. If a user puts an optional value into a string interpolation segment, it will insert either "Optional("value")" or "nil" in the resulting string. Neither of these is particularly desirable, so we propose a warning and fix-it to surface solutions to these potential mistakes.

Is there any way we could instead allow Optionals but just print them the way we print ImplicitlyUnwrappedOptionals? That's almost always how I want my Optionals to work when interpolating. To be specific, this means for .some(x) we just print x, and for .none we print "nil”.

On the other hand, when I interpolate an optional, I want it to be obvious that it is an Optional. If I didn’t want Optional(foo) in the string, I would unwrap it first.

I also disagree with the proposal that the behaviour is surprising. What else would you expect?

Having said that, I have no objection to the proposed warning.

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

If you don't think this needs a proposal

Well, that’s not up to me, so let’s wait until someone else chimes in. :)

Mark

···

On Oct 3, 2016, at 2:12 PM, Harlan Haskins <harlan@harlanhaskins.com> wrote:

, then Robert has an implementation almost done. We could submit a PR later today.

- Harlan

On Oct 3, 2016, at 4:06 PM, Mark Lacey via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Oct 3, 2016, at 11:26 AM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Oct 3, 2016, at 11:02 AM, Robert Widmann <devteam.codafi@gmail.com <mailto:devteam.codafi@gmail.com>> wrote:

Because the initializer here doesn’t take Any, it takes <T>.

I think there's a case to be made to generalize the 'Any' warning to Optional implicitly being deduced as a type variable binding in any unconstrained context. What exactly constitutes 'implicit' and 'unconstrained' is up for debate, though, and probably needs some experimentation to figure out what feels good. For instance, explicitly constructing an optional is a signal the optionality intentional. Potentially, having multiple Optional parameters binding the same type variable also increases the likelihood it's intended, for example:

func foo<T>(x: T, y: T) {}

var x: Int? = 1
var y: Int = 2
foo(x, y) // One Optional isn't unwrapped, forcing the other to promote. Maybe a mistake?
var z: Int? = 3
foo(x, z) // Two T parameters are Optional. Probably intentional?

Regardless of whether there's a more general principle we can base a warning on, string interpolation and String(describing:) are common enough pitfalls that they may just deserve special case treatment.

I think string interpolation could be handled pretty easily with a warning by extending the existing warning for Any. We just need to look at interpolation expressions and if any of the segments are optional-typed emit a warning unless they are explicitly casted to the optional type. The fixit can suggest explicit casting or using the debugDescription.

I’m not sure we really need an evolution proposal for that.

As for the more general topic of trickiness around optional injection into unconstrained generics: Yes, we should review that at some point as well. I recall seeing at least one concrete complaint about surprising behavior resulting from doing this in generic functions, but I cannot find the bug at the moment.

Mark

-Joe

~Robert Widmann

On Oct 3, 2016, at 2:00 PM, Harlan Haskins via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Unfortunately, Optional-to-Any does not currently hit this case because IIRC it doesn't promote to Any in an interpolation segment. I tested this with a ToT build yesterday.

- Harlan

On Oct 3, 2016, at 1:57 PM, Joe Groff <jgroff@apple.com <mailto:jgroff@apple.com>> wrote:

We now emit a warning whenever an optional is used as an Any. I disagree that this should be an error, but it seems reasonable to warn (if we don't already thanks to the 'Any' warning).

-Joe

On Oct 3, 2016, at 10:52 AM, Harlan Haskins via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hey all,

Julio Carrettoni, Robert Widmann, and I have been working on a proposal to mitigate something that's burned us all since Swift 1. We'd love some feedback!

It's available here: Disallow Optionals in String Interpolation · GitHub

I've posted the current draft below.

Thanks,
Harlan Haskins

Disallow Optionals in String Interpolation Segments

Proposal: SE-NNNN <https://gist.github.com/harlanhaskins/NNNN-filename.md&gt;
Authors: Harlan Haskins <https://github.com/harlanhaskins&gt;, Julio Carrettoni <https://github.com/Julioacarrettoni&gt;, Robert Widmann <https://github.com/CodaFi&gt;
Review Manager: TBD
Status: Awaiting revie
<Disallow Optionals in String Interpolation · GitHub

Swift developers frequently use string interpolation as a convenient, concise syntax for interweaving variable values with strings. The interpolation machinery, however, has surprising behavior in one specific case: Optional<T>. If a user puts an optional value into a string interpolation segment, it will insert either "Optional("value")" or "nil" in the resulting string. Neither of these is particularly desirable, so we propose a warning and fix-it to surface solutions to these potential mistakes.

Swift-evolution thread: Discussion thread topic for that proposal <https://lists.swift.org/pipermail/swift-evolution/&gt;
<Disallow Optionals in String Interpolation · GitHub

The Swift Programming Language defines string interpolation segments as "a way to construct a new String value from a mix of constants, variables, literals, and expressions". There is one type that runs counter to this definition: Optional. The .none case in particular is used to indicate the absence of a value. Moreover, its inclusion in interpolation segments leads to the dreaded "nil" in output that is often fed to UI elements. Even barring that, interpolating a non-nil optional value yields "Optional("value")", a result that is not useful even in logged output.

Given that the Optional type is never fit for display to the end user, and can often be a surprising find in the console, we propose that requesting an Optional's debug description be an explicit act. This proposal now requires a warning when using an expression of Optional type within a string interpolation segment.

<Disallow Optionals in String Interpolation · GitHub solution

The user will be warned after attempting to use an expression with type Optional<T> in a string interpolation segment. They will then be offered a fixit suggesting they explicitly request the debugDescription of the Optional value instead.

<Disallow Optionals in String Interpolation · GitHub design

Semantic analysis currently does not do much but guarantee the well-formedness of expressions in interpolation segments. These are then fed directly to String.init(stringInterpolationSegment:) and are run through the runtime reflection system to generate a description. Semantic analysis will be tweaked to inspect the result of solving an interpolation segment for an Optional and will offer a fixit in that case.

<Disallow Optionals in String Interpolation · GitHub on existing code

As this is a warning, code written before this proposal will continue to compile and run with the same semantics as before. Authors of code that makes use of this unsafe pattern will be offered a migration path to the safer, more explicit form.

<Disallow Optionals in String Interpolation · GitHub considered

A fixit that suggests a default value be inserted would be entirely appropriate (following the style of the fixit introduced in SE-0140 <https://github.com/apple/swift-evolution/blob/master/proposals/0140-bridge-optional-to-nsnull.md&gt;\).

Forbidding this pattern by hard error would make this proposal a breaking change that is out of scope for this stage of Swift's development.

A fixit that introduces a force-unwrapping would technically work as well, however it would be fixing a dangerous operation with yet another dangerous operation.

Sent from my iPad
_______________________________________________
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 think that if we're going to add this warning we should make it possible to provide a string as an alternative. It seems like it should be possible to build a ?? operator with a (T?, String) -> _StringInterpolationSomething signature that works only in a string interpolation context.

There are some types that aren't trivially constructible, or don't have clear alternatives for the nil case. Other times it might just not make sense to build a new instance simply to turn it into a string. If we're going to make people provide an alternative for optionals in this otherwise simple-to-use construct, let's make it simple to do so.

This is undoubtedly a more complex approach that could be considered separately, but I think it would be a valuable part of how developers could transition their code.

Nate

···

On Oct 3, 2016, at 5:49 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

On Mon, Oct 3, 2016, at 03:18 PM, Jordan Rose wrote:

...

We had this at one point, but we took it out because people would forget to test the nil case. I think `?? ""` or `?? nil` really is the best answer here.

But you can't write that, unless you're dealing specifically with an Optional<String>. If you try you'll get an error:

unnamed.swift:2:19: error: binary operator '??' cannot be applied to operands of type 'Int?' and 'String'
    print("x: \(x ?? "nil")")
                ~ ^ ~~~~~
unnamed.swift:2:19: note: overloads for '??' exist with these partially matching parameter lists: (T?, @autoclosure () throws -> T), (T?, @autoclosure () thro
ws -> T?)
    print("x: \(x ?? "nil")")
                  ^
This leads to writing code like "… \(x.map(String.init(describing:)) ?? "nil")" which is pretty gross.

1 Like

I like this idea. This combined with the warning for naively
interpolating an Optional would be a good solution, because now when I
see the warning I can trivially solve it with `?? "nil"`.

-Kevin Ballard

···

On Tue, Oct 4, 2016, at 10:28 AM, Nate Cook wrote:

On Oct 3, 2016, at 5:49 PM, Kevin Ballard via swift-evolution <swift- >> evolution@swift.org> wrote:

On Mon, Oct 3, 2016, at 03:18 PM, Jordan Rose wrote:

...

We had this at one point, but we took it out because people would
forget to test the nil case. I think `?? ""` or `?? nil` really is
the best answer here.

But you can't write that, unless you're dealing specifically with an
Optional<String>. If you try you'll get an error:

unnamed.swift:2:19: error: binary operator '??' cannot be applied to
operands of type 'Int?' and 'String'
    print("x: \(x ?? "nil")")
                ~ ^ ~~~~~
unnamed.swift:2:19: note: overloads for '??' exist with these
partially matching parameter lists: (T?, @autoclosure () throws ->
T), (T?, @autoclosure () thro
ws -> T?)
    print("x: \(x ?? "nil")")
                  ^
This leads to writing code like "… \(x.map(String.init(describing:))
?? "nil")" which is pretty gross.

I think that if we're going to add this warning we should make it
possible to provide a string as an alternative. It seems like it
should be possible to build a ?? operator with a (T?, String) ->
_StringInterpolationSomething signature that works only in a string
interpolation context.

There are some types that aren't trivially constructible, or don't
have clear alternatives for the nil case. Other times it might just
not make sense to build a new instance simply to turn it into a
string. If we're going to make people provide an alternative for
optionals in this otherwise simple-to-use construct, let's make it
simple to do so.

This is undoubtedly a more complex approach that could be considered
separately, but I think it would be a valuable part of how developers
could transition their code.

1 Like

...

We had this at one point, but we took it out because people would forget to test the nil case. I think `?? ""` or `?? nil` really is the best answer here.

But you can't write that, unless you're dealing specifically with an Optional<String>. If you try you'll get an error:

unnamed.swift:2:19: error: binary operator '??' cannot be applied to operands of type 'Int?' and 'String'
    print("x: \(x ?? "nil")")
                ~ ^ ~~~~~
unnamed.swift:2:19: note: overloads for '??' exist with these partially matching parameter lists: (T?, @autoclosure () throws -> T), (T?, @autoclosure () thro
ws -> T?)
    print("x: \(x ?? "nil")")
                  ^
This leads to writing code like "… \(x.map(String.init(describing:)) ?? "nil")" which is pretty gross.

I think that if we're going to add this warning we should make it possible to provide a string as an alternative. It seems like it should be possible to build a ?? operator with a (T?, String) -> _StringInterpolationSomething signature that works only in a string interpolation context.

There are some types that aren't trivially constructible, or don't have clear alternatives for the nil case. Other times it might just not make sense to build a new instance simply to turn it into a string. If we're going to make people provide an alternative for optionals in this otherwise simple-to-use construct, let's make it simple to do so.

This is undoubtedly a more complex approach that could be considered separately, but I think it would be a valuable part of how developers could transition their code.

That’s definitely more complex, and seems like a completely orthogonal feature request.

I like this idea. This combined with the warning for naively interpolating an Optional would be a good solution, because now when I see the warning I can trivially solve it with `?? "nil”`.

If you can suppress the warning with `as T?` (where T? is the type of the thing being warned on), you wouldn’t need a form that specifically printed “nil”, correct?

Mark

···

On Oct 4, 2016, at 10:29 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:
On Tue, Oct 4, 2016, at 10:28 AM, Nate Cook wrote:

On Oct 3, 2016, at 5:49 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Mon, Oct 3, 2016, at 03:18 PM, Jordan Rose wrote:

How many times do I need to repeat myself? I'm looking for a solution to
the problem where printing Optionals sanely (e.g. no "Optional(…)"
wrapper for .some values) is a PITA right now. Getting rid of the
warning does not solve this problem. This is why I like Nate Cook's idea
to enable `?? "nil"` in string interpolations, because it *does* solve
my problem. And with this tool, now the warning on printing Optionals
becomes useful because it tells me where to add `?? "nil"`. Getting rid
of the warning without the ability to add `?? "nil"` is not helpful to
me, because I don't want to print "Optional(…)".

-Kevin Ballard

···

On Tue, Oct 4, 2016, at 10:44 AM, Mark Lacey wrote:

On Oct 4, 2016, at 10:29 AM, Kevin Ballard via swift-evolution <swift- >> evolution@swift.org> wrote:

On Tue, Oct 4, 2016, at 10:28 AM, Nate Cook wrote:

On Oct 3, 2016, at 5:49 PM, Kevin Ballard via swift-evolution <swift- >>>> evolution@swift.org> wrote:

On Mon, Oct 3, 2016, at 03:18 PM, Jordan Rose wrote:

...

We had this at one point, but we took it out because people would
forget to test the nil case. I think `?? ""` or `?? nil` really is
the best answer here.

But you can't write that, unless you're dealing specifically with
an Optional<String>. If you try you'll get an error:

unnamed.swift:2:19: error: binary operator '??' cannot be applied
to operands of type 'Int?' and 'String'
    print("x: \(x ?? "nil")")
                ~ ^ ~~~~~
unnamed.swift:2:19: note: overloads for '??' exist with these
partially matching parameter lists: (T?, @autoclosure () throws ->
T), (T?, @autoclosure () thro
ws -> T?)
    print("x: \(x ?? "nil")")
                  ^
This leads to writing code like "…
\(x.map(String.init(describing:)) ?? "nil")" which is pretty gross.

I think that if we're going to add this warning we should make it
possible to provide a string as an alternative. It seems like it
should be possible to build a ?? operator with a (T?, String) ->
_StringInterpolationSomething signature that works only in a string
interpolation context.

There are some types that aren't trivially constructible, or don't
have clear alternatives for the nil case. Other times it might just
not make sense to build a new instance simply to turn it into a
string. If we're going to make people provide an alternative for
optionals in this otherwise simple-to-use construct, let's make it
simple to do so.

This is undoubtedly a more complex approach that could be considered
separately, but I think it would be a valuable part of how
developers could transition their code.

That’s definitely more complex, and seems like a completely orthogonal
feature request.

I like this idea. This combined with the warning for naively
interpolating an Optional would be a good solution, because now when
I see the warning I can trivially solve it with `?? "nil”`.

If you can suppress the warning with `as T?` (where T? is the type of
the thing being warned on), you wouldn’t need a form that specifically
printed “nil”, correct?

1 Like

A string interpolation segment can accept any type, so 'x ?? "nil"' arguably ought to be able to work by joining both sides of the ?? to 'Any', as if you'd written `(x as Any?) ?? ("nil" as Any)`. IIRC we have some artificial limitations on deducing `Any`, since it can obviously be problematic to have things like array literals automatically fall into [Any] because you accidentally mixed types, but string interpolations are loosely-typed by design. I wonder if there's anything we could do to admit it in this case.

-Joe

···

On Oct 4, 2016, at 11:06 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

On Tue, Oct 4, 2016, at 10:44 AM, Mark Lacey wrote:

On Oct 4, 2016, at 10:29 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Tue, Oct 4, 2016, at 10:28 AM, Nate Cook wrote:

On Oct 3, 2016, at 5:49 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Mon, Oct 3, 2016, at 03:18 PM, Jordan Rose wrote:

...

We had this at one point, but we took it out because people would forget to test the nil case. I think `?? ""` or `?? nil` really is the best answer here.

But you can't write that, unless you're dealing specifically with an Optional<String>. If you try you'll get an error:

unnamed.swift:2:19: error: binary operator '??' cannot be applied to operands of type 'Int?' and 'String'
    print("x: \(x ?? "nil")")
                ~ ^ ~~~~~
unnamed.swift:2:19: note: overloads for '??' exist with these partially matching parameter lists: (T?, @autoclosure () throws -> T), (T?, @autoclosure () thro
ws -> T?)
    print("x: \(x ?? "nil")")
                  ^
This leads to writing code like "… \(x.map(String.init(describing:)) ?? "nil")" which is pretty gross.

I think that if we're going to add this warning we should make it possible to provide a string as an alternative. It seems like it should be possible to build a ?? operator with a (T?, String) -> _StringInterpolationSomething signature that works only in a string interpolation context.

There are some types that aren't trivially constructible, or don't have clear alternatives for the nil case. Other times it might just not make sense to build a new instance simply to turn it into a string. If we're going to make people provide an alternative for optionals in this otherwise simple-to-use construct, let's make it simple to do so.

This is undoubtedly a more complex approach that could be considered separately, but I think it would be a valuable part of how developers could transition their code.

That’s definitely more complex, and seems like a completely orthogonal feature request.

I like this idea. This combined with the warning for naively interpolating an Optional would be a good solution, because now when I see the warning I can trivially solve it with `?? "nil”`.

If you can suppress the warning with `as T?` (where T? is the type of the thing being warned on), you wouldn’t need a form that specifically printed “nil”, correct?

How many times do I need to repeat myself? I'm looking for a solution to the problem where printing Optionals sanely (e.g. no "Optional(…)" wrapper for .some values) is a PITA right now. Getting rid of the warning does not solve this problem. This is why I like Nate Cook's idea to enable `?? "nil"` in string interpolations, because it does solve my problem. And with this tool, now the warning on printing Optionals becomes useful because it tells me where to add `?? "nil"`. Getting rid of the warning without the ability to add `?? "nil"` is not helpful to me, because I don't want to print "Optional(…)".

1 Like

...

We had this at one point, but we took it out because people would forget
to test the nil case. I think `?? ""` or `?? nil` really is the best answer
here.

But you can't write that, unless you're dealing specifically with an
Optional<String>. If you try you'll get an error:

unnamed.swift:2:19: error: binary operator '??' cannot be applied to
operands of type 'Int?' and 'String'
    print("x: \(x ?? "nil")")
                ~ ^ ~~~~~
unnamed.swift:2:19: note: overloads for '??' exist with these partially
matching parameter lists: (T?, @autoclosure () throws -> T), (T?,
@autoclosure () thro
ws -> T?)
    print("x: \(x ?? "nil")")
                  ^
This leads to writing code like "… \(x.map(String.init(describing:)) ??
"nil")" which is pretty gross.

I think that if we're going to add this warning we should make it possible
to provide a string as an alternative. It seems like it should be possible
to build a ?? operator with a (T?, String) ->
_StringInterpolationSomething signature that works only in a string
interpolation context.

There are some types that aren't trivially constructible, or don't have
clear alternatives for the nil case. Other times it might just not make
sense to build a new instance simply to turn it into a string. If we're
going to make people provide an alternative for optionals in this otherwise
simple-to-use construct, let's make it simple to do so.

This is undoubtedly a more complex approach that could be considered
separately, but I think it would be a valuable part of how developers could
transition their code.

That’s definitely more complex, and seems like a completely orthogonal
feature request.

I like this idea. This combined with the warning for naively interpolating
an Optional would be a good solution, because now when I see the warning I
can trivially solve it with `?? "nil”`.

If you can suppress the warning with `as T?` (where T? is the type of the
thing being warned on), you wouldn’t need a form that specifically printed
“nil”, correct?

How many times do I need to repeat myself? I'm looking for a solution to
the problem where printing Optionals sanely (e.g. no "Optional(…)" wrapper
for .some values) is a PITA right now. Getting rid of the warning does not
solve this problem. This is why I like Nate Cook's idea to enable `??
"nil"` in string interpolations, because it *does* solve my problem. And
with this tool, now the warning on printing Optionals becomes useful
because it tells me where to add `?? "nil"`. Getting rid of the warning
without the ability to add `?? "nil"` is not helpful to me, because I don't
want to print "Optional(…)".

I'm confused. Why not just add this to your project?

extension Optional : CustomStringConvertible {
    public var description: String {
        guard let some = self else { return "nil" }
        return String(describing: some)
    }
}

-Kevin Ballard

···

On Tue, Oct 4, 2016 at 1:06 PM, Kevin Ballard via swift-evolution < swift-evolution@swift.org> wrote:

On Tue, Oct 4, 2016, at 10:44 AM, Mark Lacey wrote:
On Oct 4, 2016, at 10:29 AM, Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:
On Tue, Oct 4, 2016, at 10:28 AM, Nate Cook wrote:
On Oct 3, 2016, at 5:49 PM, Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:
On Mon, Oct 3, 2016, at 03:18 PM, Jordan Rose wrote:

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

It's an interesting idea that needs to be written down in a separate proposal and is tangentially related to the problem we are trying to solve here and now. It is trivial to define this operator and was suggested by Charlie as new API to be added to Optional the last time improving Optionals in interpolation segments was discussed. For now we're trying to keep it light and bugfix-esque.

~Robert Widmann

2016/10/04 13:29、Kevin Ballard via swift-evolution <swift-evolution@swift.org> のメッセージ:

···

On Tue, Oct 4, 2016, at 10:28 AM, Nate Cook wrote:

On Oct 3, 2016, at 5:49 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

On Mon, Oct 3, 2016, at 03:18 PM, Jordan Rose wrote:

...

We had this at one point, but we took it out because people would forget to test the nil case. I think `?? ""` or `?? nil` really is the best answer here.

But you can't write that, unless you're dealing specifically with an Optional<String>. If you try you'll get an error:

unnamed.swift:2:19: error: binary operator '??' cannot be applied to operands of type 'Int?' and 'String'
    print("x: \(x ?? "nil")")
                ~ ^ ~~~~~
unnamed.swift:2:19: note: overloads for '??' exist with these partially matching parameter lists: (T?, @autoclosure () throws -> T), (T?, @autoclosure () thro
ws -> T?)
    print("x: \(x ?? "nil")")
                  ^
This leads to writing code like "… \(x.map(String.init(describing:)) ?? "nil")" which is pretty gross.

I think that if we're going to add this warning we should make it possible to provide a string as an alternative. It seems like it should be possible to build a ?? operator with a (T?, String) -> _StringInterpolationSomething signature that works only in a string interpolation context.

There are some types that aren't trivially constructible, or don't have clear alternatives for the nil case. Other times it might just not make sense to build a new instance simply to turn it into a string. If we're going to make people provide an alternative for optionals in this otherwise simple-to-use construct, let's make it simple to do so.

This is undoubtedly a more complex approach that could be considered separately, but I think it would be a valuable part of how developers could transition their code.

I like this idea. This combined with the warning for naively interpolating an Optional would be a good solution, because now when I see the warning I can trivially solve it with `?? "nil"`.

-Kevin Ballard

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

We generally don't consider existential types as a meet of two non-existential types because (1) finding the most-precise common existential type involves enumerating all the known conformances of each type, which is problematic for a number of reasons, and (2) it can be done for two completely arbitrary types because of Any, but we don't want things like (flag ? "foo" : 123) to type-check without some sort of type annotation.

John.

···

On Oct 4, 2016, at 11:47 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Oct 4, 2016, at 11:06 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Tue, Oct 4, 2016, at 10:44 AM, Mark Lacey wrote:

On Oct 4, 2016, at 10:29 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Tue, Oct 4, 2016, at 10:28 AM, Nate Cook wrote:

On Oct 3, 2016, at 5:49 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Mon, Oct 3, 2016, at 03:18 PM, Jordan Rose wrote:

...

We had this at one point, but we took it out because people would forget to test the nil case. I think `?? ""` or `?? nil` really is the best answer here.

But you can't write that, unless you're dealing specifically with an Optional<String>. If you try you'll get an error:

unnamed.swift:2:19: error: binary operator '??' cannot be applied to operands of type 'Int?' and 'String'
    print("x: \(x ?? "nil")")
                ~ ^ ~~~~~
unnamed.swift:2:19: note: overloads for '??' exist with these partially matching parameter lists: (T?, @autoclosure () throws -> T), (T?, @autoclosure () thro
ws -> T?)
    print("x: \(x ?? "nil")")
                  ^
This leads to writing code like "… \(x.map(String.init(describing:)) ?? "nil")" which is pretty gross.

I think that if we're going to add this warning we should make it possible to provide a string as an alternative. It seems like it should be possible to build a ?? operator with a (T?, String) -> _StringInterpolationSomething signature that works only in a string interpolation context.

There are some types that aren't trivially constructible, or don't have clear alternatives for the nil case. Other times it might just not make sense to build a new instance simply to turn it into a string. If we're going to make people provide an alternative for optionals in this otherwise simple-to-use construct, let's make it simple to do so.

This is undoubtedly a more complex approach that could be considered separately, but I think it would be a valuable part of how developers could transition their code.

That’s definitely more complex, and seems like a completely orthogonal feature request.

I like this idea. This combined with the warning for naively interpolating an Optional would be a good solution, because now when I see the warning I can trivially solve it with `?? "nil”`.

If you can suppress the warning with `as T?` (where T? is the type of the thing being warned on), you wouldn’t need a form that specifically printed “nil”, correct?

How many times do I need to repeat myself? I'm looking for a solution to the problem where printing Optionals sanely (e.g. no "Optional(…)" wrapper for .some values) is a PITA right now. Getting rid of the warning does not solve this problem. This is why I like Nate Cook's idea to enable `?? "nil"` in string interpolations, because it does solve my problem. And with this tool, now the warning on printing Optionals becomes useful because it tells me where to add `?? "nil"`. Getting rid of the warning without the ability to add `?? "nil"` is not helpful to me, because I don't want to print "Optional(…)".

A string interpolation segment can accept any type, so 'x ?? "nil"' arguably ought to be able to work by joining both sides of the ?? to 'Any', as if you'd written `(x as Any?) ?? ("nil" as Any)`. IIRC we have some artificial limitations on deducing `Any`, since it can obviously be problematic to have things like array literals automatically fall into [Any] because you accidentally mixed types, but string interpolations are loosely-typed by design. I wonder if there's anything we could do to admit it in this case.

Because that's globally changing the behavior of Optional in a way
that's very surprising.

-Kevin

···

On Tue, Oct 4, 2016, at 11:34 AM, Xiaodi Wu wrote:

On Tue, Oct 4, 2016 at 1:06 PM, Kevin Ballard via swift-evolution <swift- > evolution@swift.org> wrote:

__

On Tue, Oct 4, 2016, at 10:44 AM, Mark Lacey wrote:

On Oct 4, 2016, at 10:29 AM, Kevin Ballard via swift-evolution <swift- >>>> evolution@swift.org> wrote:

On Tue, Oct 4, 2016, at 10:28 AM, Nate Cook wrote:

On Oct 3, 2016, at 5:49 PM, Kevin Ballard via swift-evolution <swift- >>>>>> evolution@swift.org> wrote:

On Mon, Oct 3, 2016, at 03:18 PM, Jordan Rose wrote:

...

We had this at one point, but we took it out because people
would forget to test the nil case. I think `?? ""` or `?? nil`
really is the best answer here.

But you can't write that, unless you're dealing specifically with
an Optional<String>. If you try you'll get an error:

unnamed.swift:2:19: error: binary operator '??' cannot be applied
to operands of type 'Int?' and 'String'
    print("x: \(x ?? "nil")")
                ~ ^ ~~~~~
unnamed.swift:2:19: note: overloads for '??' exist with these
partially matching parameter lists: (T?, @autoclosure () throws
-> T), (T?, @autoclosure () thro
ws -> T?)
    print("x: \(x ?? "nil")")
                  ^
This leads to writing code like "…
\(x.map(String.init(describing:)) ?? "nil")" which is pretty
gross.

I think that if we're going to add this warning we should make it
possible to provide a string as an alternative. It seems like it
should be possible to build a ?? operator with a (T?, String) ->
_StringInterpolationSomething signature that works only in a
string interpolation context.

There are some types that aren't trivially constructible, or don't
have clear alternatives for the nil case. Other times it might
just not make sense to build a new instance simply to turn it into
a string. If we're going to make people provide an alternative for
optionals in this otherwise simple-to-use construct, let's make it
simple to do so.

This is undoubtedly a more complex approach that could be
considered separately, but I think it would be a valuable part of
how developers could transition their code.

That’s definitely more complex, and seems like a completely
orthogonal feature request.

I like this idea. This combined with the warning for naively
interpolating an Optional would be a good solution, because now
when I see the warning I can trivially solve it with `?? "nil”`.

If you can suppress the warning with `as T?` (where T? is the type
of the thing being warned on), you wouldn’t need a form that
specifically printed “nil”, correct?

How many times do I need to repeat myself? I'm looking for a solution
to the problem where printing Optionals sanely (e.g. no "Optional(…)"
wrapper for .some values) is a PITA right now. Getting rid of the
warning does not solve this problem. This is why I like Nate Cook's
idea to enable `?? "nil"` in string interpolations, because it *does*
solve my problem. And with this tool, now the warning on printing
Optionals becomes useful because it tells me where to add `?? "nil"`.
Getting rid of the warning without the ability to add `?? "nil"` is
not helpful to me, because I don't want to print "Optional(…)".

I'm confused. Why not just add this to your project?

extension Optional : CustomStringConvertible {
    public var description: String {
        guard let some = self else { return "nil" }
        return String(describing: some)
    }
}
1 Like

...

We had this at one point, but we took it out because people would forget
to test the nil case. I think `?? ""` or `?? nil` really is the best answer
here.

But you can't write that, unless you're dealing specifically with an
Optional<String>. If you try you'll get an error:

unnamed.swift:2:19: error: binary operator '??' cannot be applied to
operands of type 'Int?' and 'String'
    print("x: \(x ?? "nil")")
                ~ ^ ~~~~~
unnamed.swift:2:19: note: overloads for '??' exist with these partially
matching parameter lists: (T?, @autoclosure () throws -> T), (T?,
@autoclosure () thro
ws -> T?)
    print("x: \(x ?? "nil")")
                  ^
This leads to writing code like "… \(x.map(String.init(describing:)) ??
"nil")" which is pretty gross.

I think that if we're going to add this warning we should make it possible
to provide a string as an alternative. It seems like it should be possible
to build a ?? operator with a (T?, String) ->
_StringInterpolationSomething signature that works only in a string
interpolation context.

There are some types that aren't trivially constructible, or don't have
clear alternatives for the nil case. Other times it might just not make
sense to build a new instance simply to turn it into a string. If we're
going to make people provide an alternative for optionals in this otherwise
simple-to-use construct, let's make it simple to do so.

This is undoubtedly a more complex approach that could be considered
separately, but I think it would be a valuable part of how developers could
transition their code.

That’s definitely more complex, and seems like a completely orthogonal
feature request.

I like this idea. This combined with the warning for naively interpolating
an Optional would be a good solution, because now when I see the warning I
can trivially solve it with `?? "nil”`.

If you can suppress the warning with `as T?` (where T? is the type of the
thing being warned on), you wouldn’t need a form that specifically printed
“nil”, correct?

How many times do I need to repeat myself? I'm looking for a solution to
the problem where printing Optionals sanely (e.g. no "Optional(…)" wrapper
for .some values) is a PITA right now. Getting rid of the warning does not
solve this problem. This is why I like Nate Cook's idea to enable `??
"nil"` in string interpolations, because it *does* solve my problem. And
with this tool, now the warning on printing Optionals becomes useful
because it tells me where to add `?? "nil"`. Getting rid of the warning
without the ability to add `?? "nil"` is not helpful to me, because I don't
want to print "Optional(…)".

I'm confused. Why not just add this to your project?

extension Optional : CustomStringConvertible {
    public var description: String {
        guard let some = self else { return "nil" }
        return String(describing: some)
    }
}

Because that's globally changing the behavior of Optional in a way that's
very surprising.

Agreed, but I thought that's what you were asking for above?

···

On Tue, Oct 4, 2016 at 1:49 PM, Kevin Ballard <kevin@sb.org> wrote:

On Tue, Oct 4, 2016, at 11:34 AM, Xiaodi Wu wrote:
On Tue, Oct 4, 2016 at 1:06 PM, Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:
On Tue, Oct 4, 2016, at 10:44 AM, Mark Lacey wrote:
On Oct 4, 2016, at 10:29 AM, Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:
On Tue, Oct 4, 2016, at 10:28 AM, Nate Cook wrote:
On Oct 3, 2016, at 5:49 PM, Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:
On Mon, Oct 3, 2016, at 03:18 PM, Jordan Rose wrote:

-Kevin

I wanted to change the behavior of string interpolation specifically,
not the behavior of `String(describing: someOptionalValue)`.

-Kevin

···

On Tue, Oct 4, 2016, at 12:01 PM, Xiaodi Wu wrote:

On Tue, Oct 4, 2016 at 1:49 PM, Kevin Ballard <kevin@sb.org> wrote:

__
On Tue, Oct 4, 2016, at 11:34 AM, Xiaodi Wu wrote:

On Tue, Oct 4, 2016 at 1:06 PM, Kevin Ballard via swift-evolution >>> <swift-evolution@swift.org> wrote:

__

On Tue, Oct 4, 2016, at 10:44 AM, Mark Lacey wrote:

On Oct 4, 2016, at 10:29 AM, Kevin Ballard via swift-evolution >>>>>> <swift-evolution@swift.org> wrote:

On Tue, Oct 4, 2016, at 10:28 AM, Nate Cook wrote:

On Oct 3, 2016, at 5:49 PM, Kevin Ballard via swift-evolution >>>>>>>> <swift-evolution@swift.org> wrote:

On Mon, Oct 3, 2016, at 03:18 PM, Jordan Rose wrote:

...

We had this at one point, but we took it out because people
would forget to test the nil case. I think `?? ""` or `?? nil`
really is the best answer here.

But you can't write that, unless you're dealing specifically
with an Optional<String>. If you try you'll get an error:

unnamed.swift:2:19: error: binary operator '??' cannot be
applied to operands of type 'Int?' and 'String'
    print("x: \(x ?? "nil")")
                ~ ^ ~~~~~
unnamed.swift:2:19: note: overloads for '??' exist with these
partially matching parameter lists: (T?, @autoclosure () throws
-> T), (T?, @autoclosure () thro
ws -> T?)
    print("x: \(x ?? "nil")")
                  ^
This leads to writing code like "…
\(x.map(String.init(describing:)) ?? "nil")" which is pretty
gross.

I think that if we're going to add this warning we should make
it possible to provide a string as an alternative. It seems like
it should be possible to build a ?? operator with a (T?, String)
-> _StringInterpolationSomething signature that works only in a
string interpolation context.

There are some types that aren't trivially constructible, or
don't have clear alternatives for the nil case. Other times it
might just not make sense to build a new instance simply to turn
it into a string. If we're going to make people provide an
alternative for optionals in this otherwise simple-to-use
construct, let's make it simple to do so.

This is undoubtedly a more complex approach that could be
considered separately, but I think it would be a valuable part
of how developers could transition their code.

That’s definitely more complex, and seems like a completely
orthogonal feature request.

I like this idea. This combined with the warning for naively
interpolating an Optional would be a good solution, because now
when I see the warning I can trivially solve it with `?? "nil”`.

If you can suppress the warning with `as T?` (where T? is the type
of the thing being warned on), you wouldn’t need a form that
specifically printed “nil”, correct?

How many times do I need to repeat myself? I'm looking for a
solution to the problem where printing Optionals sanely (e.g. no
"Optional(…)" wrapper for .some values) is a PITA right now.
Getting rid of the warning does not solve this problem. This is why
I like Nate Cook's idea to enable `?? "nil"` in string
interpolations, because it *does* solve my problem. And with this
tool, now the warning on printing Optionals becomes useful because
it tells me where to add `?? "nil"`. Getting rid of the warning
without the ability to add `?? "nil"` is not helpful to me, because
I don't want to print "Optional(…)".

I'm confused. Why not just add this to your project?

extension Optional : CustomStringConvertible {
    public var description: String {
        guard let some = self else { return "nil" }
        return String(describing: some)
    }
}

Because that's globally changing the behavior of Optional in a way
that's very surprising.

Agreed, but I thought that's what you were asking for above?

...

We had this at one point, but we took it out because people would forget
to test the nil case. I think `?? ""` or `?? nil` really is the best answer
here.

But you can't write that, unless you're dealing specifically with an
Optional<String>. If you try you'll get an error:

unnamed.swift:2:19: error: binary operator '??' cannot be applied to
operands of type 'Int?' and 'String'
    print("x: \(x ?? "nil")")
                ~ ^ ~~~~~
unnamed.swift:2:19: note: overloads for '??' exist with these partially
matching parameter lists: (T?, @autoclosure () throws -> T), (T?,
@autoclosure () thro
ws -> T?)
    print("x: \(x ?? "nil")")
                  ^
This leads to writing code like "… \(x.map(String.init(describing:)) ??
"nil")" which is pretty gross.

I think that if we're going to add this warning we should make it possible
to provide a string as an alternative. It seems like it should be possible
to build a ?? operator with a (T?, String) ->
_StringInterpolationSomething signature that works only in a string
interpolation context.

There are some types that aren't trivially constructible, or don't have
clear alternatives for the nil case. Other times it might just not make
sense to build a new instance simply to turn it into a string. If we're
going to make people provide an alternative for optionals in this otherwise
simple-to-use construct, let's make it simple to do so.

This is undoubtedly a more complex approach that could be considered
separately, but I think it would be a valuable part of how developers could
transition their code.

That’s definitely more complex, and seems like a completely orthogonal
feature request.

I like this idea. This combined with the warning for naively interpolating
an Optional would be a good solution, because now when I see the warning I
can trivially solve it with `?? "nil”`.

If you can suppress the warning with `as T?` (where T? is the type of the
thing being warned on), you wouldn’t need a form that specifically printed
“nil”, correct?

How many times do I need to repeat myself? I'm looking for a solution to
the problem where printing Optionals sanely (e.g. no "Optional(…)" wrapper
for .some values) is a PITA right now. Getting rid of the warning does not
solve this problem. This is why I like Nate Cook's idea to enable `??
"nil"` in string interpolations, because it *does* solve my problem. And
with this tool, now the warning on printing Optionals becomes useful
because it tells me where to add `?? "nil"`. Getting rid of the warning
without the ability to add `?? "nil"` is not helpful to me, because I don't
want to print "Optional(…)".

I'm confused. Why not just add this to your project?

extension Optional : CustomStringConvertible {
    public var description: String {
        guard let some = self else { return "nil" }
        return String(describing: some)
    }
}

Because that's globally changing the behavior of Optional in a way that's
very surprising.

Agreed, but I thought that's what you were asking for above?

I wanted to change the behavior of string interpolation specifically, not
the behavior of `String(describing: someOptionalValue)`.

It hadn't occurred to me that this was on the table. It would be very
surprising for string interpolation to behave differently from
`String.init(describing:)`, would it not?

-Kevin

···

On Tue, Oct 4, 2016 at 7:16 PM, Kevin Ballard <kevin@sb.org> wrote:

On Tue, Oct 4, 2016, at 12:01 PM, Xiaodi Wu wrote:
On Tue, Oct 4, 2016 at 1:49 PM, Kevin Ballard <kevin@sb.org> wrote:
On Tue, Oct 4, 2016, at 11:34 AM, Xiaodi Wu wrote:
On Tue, Oct 4, 2016 at 1:06 PM, Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:
On Tue, Oct 4, 2016, at 10:44 AM, Mark Lacey wrote:
On Oct 4, 2016, at 10:29 AM, Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:
On Tue, Oct 4, 2016, at 10:28 AM, Nate Cook wrote:
On Oct 3, 2016, at 5:49 PM, Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:
On Mon, Oct 3, 2016, at 03:18 PM, Jordan Rose wrote:

@harlanhaskins @codafi @nnnnnnnn @Joe_Groff

One of my biggest frustrations with Swift is attempting to satisfy compiler warnings about optional types. I want to print the value of a variable without adornment.

The "Fix It" recommendations are not helpful when there is a type mismatch using string interpolation.

var a: Int? = 5
print("a: \(a)") // a: Optional(5)

// Fix It 1: Use String(describing:) to silence
print("a: \(String(describing: a))") // a: Optional(5) <=== Not what I want!

// Fix It 2: Does not compile
print("a: \(a ?? "nil")") // Error: Cannot convert value of type 'String' to expected argument type 'Int'

If I try to work around the compiler error, I create more issues:

// Workaround 1: Check and force unwrap
print("a: \(a != nil ? "\(a!)" : "nil")") // a: 5 <=== Overly verbose

// Workaround 2: a: 5 <==== Duplicating code
if let a {
    print("a: \(a)")
} else {
    print("a: nil")
}

// Workaround 3: Calling description is discouraged because it "infects code" with needing `CustomStringConvertible` and the like for generic constraints
print("a: \(a?.description ?? "nil")")

When I write non-user-facing code, I should not have to deal with "presentation rules."

Solutions Explored

Can we add a new "Fix It" that works for type mismatches or an enhancement to Swift that would be less verbose for non-user-facing string output?

@ole Suggested the ??? operator

infix operator ???: NilCoalescingPrecedence

public func ???<T>(value: T?, defaultValue: @autoclosure () -> String) -> String {
    guard let value else { return defaultValue() }
    return String(describing:value)
}

var x: Int? = 15
print("x: \(x ??? "nil")") // 15
x = nil
print("x: \(x ??? "nil")") // nil

Another option is the orNil extension on Optional.

extension Optional {
    var orNil: String {
        self == nil ? "nil" : "\(self!)"
    }
}

var b: Int? = nil
print("b: \(b.orNil)") // nil
b = 5
print("b: \(b.orNil)") // 5

Both of these solutions are more concise and allow me to solve the "Fix It" when the types don't match the String type.

You can make this work with print("a: \((a ?? "nil") as Any)")

Also, please don't revive threads so old that they came from the mailing list. It's much better to make a new post and link to the old one.

1 Like

Thanks, but I don't want to type that much code. It's overly verbose for simply printing a value.

This thread is 8 years old!! Here's a more recent discussion on the same topic:

3 Likes

The inner parens aren't needed; in other words, instead of writing a ?? "nil", you can simply write a ?? "nil" as Any to indicate the coercion. As others have mentioned, though, please start a new thread as this one dates from 2016 to discuss further.

1 Like