Optional to String conversion

I’m adding runtime functionality to support optional casts and ran into some Optional to String conversion behavior that is currently somewhat accidental—it will break when I add functionality. I want to understand the desired behavior before doing extra work to fix it.

Currently swift does this:

print(Int?(3))

Optional(3)

print(String?("meow"))

Optional("meow")

I think swift should do this:

print(Int?(3))

3

print(String?("meow"))

"meow"

debugPrint(Int?(3))

Optional(3)

debugPrint(String?("meow"))

Optional("meow")

When a value already knows how to represent itself as a string, I don't think that the string "Optional" belongs in the textual representation.

When debugging data structures, it makes sense to include the "Optional" indicator.

What was the intention here and what do people think is the desired behavior?

Andy

This behavior was not accidental. Here's the rationale:

If an Optional is nil, it should somehow indicate that. Whatever the
representation is, it would be engineer-oriented and not suitable for
end users. Thus, Optional should never be printed to users, it would
only ever be presented to engineers, and thus its print() and
debugPrint() representations should be the same.

Dmitri

···

On Fri, Dec 4, 2015 at 10:00 PM, Andrew Trick <atrick@apple.com> wrote:

I’m adding runtime functionality to support optional casts and ran into some Optional to String conversion behavior that is currently somewhat accidental—it will break when I add functionality. I want to understand the desired behavior before doing extra work to fix it.

Currently swift does this:

print(Int?(3))

Optional(3)

print(String?("meow"))

Optional("meow")

I think swift should do this:

print(Int?(3))

3

print(String?("meow"))

"meow"

debugPrint(Int?(3))

Optional(3)

debugPrint(String?("meow"))

Optional("meow")

When a value already knows how to represent itself as a string, I don't think that the string "Optional" belongs in the textual representation.

When debugging data structures, it makes sense to include the "Optional" indicator.

What was the intention here and what do people think is the desired behavior?

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/

There are unfortunate output ambiguities if you don't include it.

Consider the output of all of these:

print(String?("test"))
print(String?("nil"))
print(String?.None)

The current output is unambiguous:

print(String?("test"))
    Optional("test")
print(String?("nil"))
    Optional("nil")
print(String?.None)
    nil

···

On Dec 4, 2015, at 10:00 PM, Andrew Trick <atrick@apple.com> wrote:

I’m adding runtime functionality to support optional casts and ran into some Optional to String conversion behavior that is currently somewhat accidental—it will break when I add functionality. I want to understand the desired behavior before doing extra work to fix it.

Currently swift does this:

print(Int?(3))

Optional(3)

print(String?("meow"))

Optional("meow")

I think swift should do this:

print(Int?(3))

3

print(String?("meow"))

"meow"

debugPrint(Int?(3))

Optional(3)

debugPrint(String?("meow"))

Optional("meow")

When a value already knows how to represent itself as a string, I don't think that the string "Optional" belongs in the textual representation.

--
Greg Parker gparker@apple.com Runtime Wrangler

I think you're saying that it's the programmer's responsibility not to construct a user-visible String from an Optional.

By accidental, I mean that Optional does not conform to CustomStringConvertible. If we want "Optional" to be part of the string conversion, then this conformance needs to exist, or I need to add some special handling of Optional. Do you see any problem with me adding that conformance?

Incidentally, when *do* we want debug string conversion to deviate? Or is it just a customization point we anticipate to be useful?

Andy

···

On Dec 4, 2015, at 10:05 PM, Dmitri Gribenko <gribozavr@gmail.com> wrote:

On Fri, Dec 4, 2015 at 10:00 PM, Andrew Trick <atrick@apple.com> wrote:

I’m adding runtime functionality to support optional casts and ran into some Optional to String conversion behavior that is currently somewhat accidental—it will break when I add functionality. I want to understand the desired behavior before doing extra work to fix it.

Currently swift does this:

print(Int?(3))

Optional(3)

print(String?("meow"))

Optional("meow")

I think swift should do this:

print(Int?(3))

3

print(String?("meow"))

"meow"

debugPrint(Int?(3))

Optional(3)

debugPrint(String?("meow"))

Optional("meow")

When a value already knows how to represent itself as a string, I don't think that the string "Optional" belongs in the textual representation.

When debugging data structures, it makes sense to include the "Optional" indicator.

What was the intention here and what do people think is the desired behavior?

This behavior was not accidental. Here's the rationale:

If an Optional is nil, it should somehow indicate that. Whatever the
representation is, it would be engineer-oriented and not suitable for
end users. Thus, Optional should never be printed to users, it would
only ever be presented to engineers, and thus its print() and
debugPrint() representations should be the same.

Dmitri

I’m adding runtime functionality to support optional casts and ran into some Optional to String conversion behavior that is currently somewhat accidental—it will break when I add functionality. I want to understand the desired behavior before doing extra work to fix it.

Currently swift does this:

print(Int?(3))

Optional(3)

print(String?("meow"))

Optional("meow")

I think swift should do this:

print(Int?(3))

3

print(String?("meow"))

"meow"

debugPrint(Int?(3))

Optional(3)

debugPrint(String?("meow"))

Optional("meow")

When a value already knows how to represent itself as a string, I don't think that the string "Optional" belongs in the textual representation.

When debugging data structures, it makes sense to include the "Optional" indicator.

What was the intention here and what do people think is the desired behavior?

This behavior was not accidental. Here's the rationale:

If an Optional is nil, it should somehow indicate that. Whatever the
representation is, it would be engineer-oriented and not suitable for
end users. Thus, Optional should never be printed to users, it would
only ever be presented to engineers, and thus its print() and
debugPrint() representations should be the same.

Dmitri

I think you're saying that it's the programmer's responsibility not to construct a user-visible String from an Optional.

No, the responsibility is not to display it to users. It is OK to e.g., log it.

By accidental, I mean that Optional does not conform to CustomStringConvertible. If we want "Optional" to be part of the string conversion, then this conformance needs to exist, or I need to add some special handling of Optional. Do you see any problem with me adding that conformance?

Yes, the same behavior should also apply to any user-defined type: if
there is only CustomDebugStringConvertible conformance, it should be
used for String(x) initialization.

Incidentally, when *do* we want debug string conversion to deviate? Or is it just a customization point we anticipate to be useful?

For example, in String: the user-presentable representation is the
string itself, the engineer-oriented representation shows all special
characters as escape sequences.

Dmitri

···

On Fri, Dec 4, 2015 at 10:21 PM, Andrew Trick <atrick@apple.com> wrote:

On Dec 4, 2015, at 10:05 PM, Dmitri Gribenko <gribozavr@gmail.com> wrote:
On Fri, Dec 4, 2015 at 10:00 PM, Andrew Trick <atrick@apple.com> wrote:

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/

I understand, thanks for the explanation. But Optionals are not like
other user-defined types in this respect. Let me clarify with an
example...

class A : CustomStringConvertible, CustomDebugStringConvertible {
    init() {}
    var description: String { get { return "Apple" } }
    var debugDescription: String { get { return "Swift" } }
}

print(A?(A()) is CustomStringConvertible)
// true

// Now, Optional does not conform to CustomStringConvertible, but as
// you can see above it is convertible to CustomStringConvertible.
// As print_unlocked is currently written, the behavior should be:
print(String(A?(A())))
Apple

// But as you explained, that is not the behavior we want. We could fix
// it by making Optional counter-intuitively conform to
// CustomStringConvertible. This prevents down conversion to the wrapped
// type, instead printing "Optional" and the payload's debug string.

extension Optional : CustomStringConvertible {
  /// A textual representation of `self`, suitable for debugging.
  ///
  /// Optional has no representation suitable for an output
  /// stream. Consequently, String conversion should always produce a
  /// debug string indicating the Optional type and including the
  /// debug representation of the wrapped type.
  ///
  /// Optional must conform to CustomStringConvertible to avoid
  /// automatic conversion to its wrapped type during String
  /// conversion.
  public var description: String {
    return debugDescription
  }
}
print(String(A?(A())))
Optional(Swift)

I think this is the way to go.

Andy

···

On Dec 4, 2015, at 10:26 PM, Dmitri Gribenko <gribozavr@gmail.com> wrote:

On Fri, Dec 4, 2015 at 10:21 PM, Andrew Trick <atrick@apple.com <mailto:atrick@apple.com>> wrote:

On Dec 4, 2015, at 10:05 PM, Dmitri Gribenko <gribozavr@gmail.com> wrote:

On Fri, Dec 4, 2015 at 10:00 PM, Andrew Trick <atrick@apple.com> wrote:

I’m adding runtime functionality to support optional casts and ran into some Optional to String conversion behavior that is currently somewhat accidental—it will break when I add functionality. I want to understand the desired behavior before doing extra work to fix it.

Currently swift does this:

print(Int?(3))

Optional(3)

print(String?("meow"))

Optional("meow")

I think swift should do this:

print(Int?(3))

3

print(String?("meow"))

"meow"

debugPrint(Int?(3))

Optional(3)

debugPrint(String?("meow"))

Optional("meow")

When a value already knows how to represent itself as a string, I don't think that the string "Optional" belongs in the textual representation.

When debugging data structures, it makes sense to include the "Optional" indicator.

What was the intention here and what do people think is the desired behavior?

This behavior was not accidental. Here's the rationale:

If an Optional is nil, it should somehow indicate that. Whatever the
representation is, it would be engineer-oriented and not suitable for
end users. Thus, Optional should never be printed to users, it would
only ever be presented to engineers, and thus its print() and
debugPrint() representations should be the same.

Dmitri

I think you're saying that it's the programmer's responsibility not to construct a user-visible String from an Optional.

No, the responsibility is not to display it to users. It is OK to e.g., log it.

By accidental, I mean that Optional does not conform to CustomStringConvertible. If we want "Optional" to be part of the string conversion, then this conformance needs to exist, or I need to add some special handling of Optional. Do you see any problem with me adding that conformance?

Yes, the same behavior should also apply to any user-defined type: if
there is only CustomDebugStringConvertible conformance, it should be
used for String(x) initialization.

Incidentally, when *do* we want debug string conversion to deviate? Or is it just a customization point we anticipate to be useful?

For example, in String: the user-presentable representation is the
string itself, the engineer-oriented representation shows all special
characters as escape sequences.

Dmitri