[swift-evolution] Proposal: Deprecate optionals in string interpolation

Charlie Monroe charlie at charliemonroe.net
Wed May 25 02:01:40 CDT 2016

> Aha, now I understand :-)
> Perhaps "in order to detect potentially undesired behavior at compile time" would be even clearer?

Sounds much better, thanks!

> - there may be other types you may not want to use for interpolation - as mentioned in the proposal, e.g. private data structures that would expose something you want to keep private, various enum values, etc. Which is why I've started thinking about making a protocol that would indicate the type is discouraged being "interpoled". I've thought about this and decided to make a more robust and customizable solution.
> I'm not sure if this is a strong enough motivation.
> The customization point already provided by the language is CustomStringConvertible. That's the place where one can provide a readable description, hiding implementation details.

Yes, but this is the other way around. Since it is allowed to interpole types that are neither CustomStringConvertible or CustomDebugStringConvertible, it might be a good idea to take an approach from the other side as well and disallow some of them from being interpoled.

>  - both .description and .debugDescription are mentioned in alternatives for the Fix-It.
> The direct use of .description and .debugDescription is discouraged in the API docs, so I don't think this is a viable option.

Two options then:

a) introduce a new variable on Optional, e.g. .optionalDescription which would stringify the Optional into what's the current behavior. Also, .valueDescription could be introduced, which would either print "nil" or description of the value it's wrapping (by invoking .description on values that are CustomStringConvertible, or using printDebug).

b) within the declaration of these variables on Optional, specify that this is the designated way to stringify the Optional. There is IMHO no reason other than both values being used internally for various tasks and providing a user unfriendly representation of the object. Both .description and .debugDescription should have no side-effects.

>  There are many people oposing this and expecting the Optional() wrap around the value, indicating the actual type. Actually, including me - I agree it can be useful for some types of debugging since in what you wrote further, there'd be no difference between description of [1, 2, 3] (i.e. [Int]) and Optional([1, 2, 3]) (i.e. [Int]?).
> There's also no difference between print(Int(1)) and print(UInt(1)): they both output just "1".

Good point.

> I presume that it shouldn't be a problem to change it to use CustomStringConvertible instead.

That's true, but you rarely use print directly on the object (at least I don't), since you usually need a bit more context for it, so you use string interpolation to add the context - and sometimes you might want the optional with its current behavior.

There are several approaches this propsal can take:

1) The stdlib will use .description on Optionals which would either return "nil", or would stringify the value - where .description would be used if the value is CustomStringConvertible, otherwise, debugPrint would be used as until now. 

This would produce no warnings. print(optional) would still print Optional(value), but print("\(optional)") would print just value.

Pros: Nothing to be done by the user to migrate current code.
Cons: Still can result in unexpected results since it may return "nil". The user may be unaware that the value being interpoled is optional. (e.g. myURL.path, which when nonnil, always starts with "/"). Also, it breaks any code depending on the current behavior.

2) Deprecate interpolation of Optionals (and nothing else), issue a warning and offer a fix via either cast to as Any, or by using .description, .debugDescription or .optionalDescription (or whatever it would be called so that .description or .debugDescription aren't invoked directly).

Optionally, there could be .valueDescription on Optional which would print either "nil" or the value as proposed in (1).

Pros: The user has full control over what's printed out.
Cons: Requires some action from the user to migrate current code.

3) Make the solution more robust and introduce the Uninterpolable protocol, which would generate a warning for interpolation of Optionals and other custom types.

The Fix-It would offer the same for Optionals as (2). For your custom types, no Fix-It will be offered - it's up to you.

Pros: You can make custom types generate a warning when interpoled + the Pros from (2).
Cons: Same as (2).

> The above code also hints that there's another odd behavior in the current implementation:
> struct Foo: CustomStringConvertible, CustomDebugStringConvertible {
>     var description: String { return "normal" }
>     var debugDescription: String { return "debug" }
> }
> let f = Foo()
> let of = Optional(f)
> print(f)        // "normal": ok
> debugPrint(f)   // "debug": ok
> print(of)       // "Optional(debug)": unexpected
> debugPrint(of)  // "Optional(debug)": ok
> --
> Nicola

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160525/a353cc6f/attachment.html>

More information about the swift-evolution mailing list