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

Nicola Salmoria nicola.salmoria at gmail.com
Tue May 24 04:14:46 CDT 2016


Charlie Monroe via swift-evolution <swift-evolution at ...> writes:

> 
> I've jotted up a proposal here:
> https://gist.github.com/charlieMonroe/82e1519dd2b57029f69bc7abe99d7385
> 
> Please let me know if there are any comments to it.

Hi, sorry for arriving late but I had missed this thread.

I happen to have submitted a couple PRs related to this problem just last week:
https://github.com/apple/swift/pull/2551
https://github.com/apple/swift-corelibs-xctest/pull/110

In this case, the issue is not just with Optional's string interpolation,
but with Swift's implicit promotion. XCTAssertEqual()'s signature is:

public func XCTAssertEqual<T : Equatable>(_ expression1: @autoclosure ()
throws -> T?, _ expression2: @autoclosure () throws -> T?, _ message:
@autoclosure () -> String = "", file: StaticString = #file, line: UInt =
#line) -> Void

so in a test you would call it like

XCTAssertEqual(1, 2, "message")

but then the values are implicitly promoted to Optional<Int>, and when the
test fails you get the confusing message

XCTAssertEqual failed: ("Optional(1)") is not equal to ("Optional(2)") - message

which is clearly unexpected, since the parameters weren't optionals at all.

My proposed solution to this was to add an overload to XCTAssertEqual()
where the parameters aren't optionals, to prevent implicit promotion and
produce the expected output. It hadn't occurred to me that it might have
been desirable to change the Optional string interpolation altogether.

Regarding the proposal:

> This proposal suggests deprecating string interpolation of Optional in
order to prevent unexpected results at compile time.

I assume you mean "at runtime" here?

I think the detailed design needs  some more thought. The "Uninterpolable"
protocol, and suggesting to cast "as Any" in a Fix-it both seem hacks.

I'm not even sure if the general direction of a compile time warning is the
right one, and if the problem wouldn't be better solved by simply not making
Optional put "Optional()" around the value in its .description.

Arguably, if you do this:

print([1, 2, 3])

you get "[1, 2, 3]", not "Array([1, 2, 3])". So why should Optional behave
differently?

It would seem to me that the most obvious solution would be to make Optional
conform to CustomStringConvertible, like this:

extension Optional: CustomStringConvertible {
    public var description: String {
        switch self {
        case .some(let x): return "\(x)"
        case .none: return "nil"
        }
    }
}

interestingly, this doesn't quite seem to work at the moment:

let o = Optional(1)

print(o.description)        // "1", ok
print(o.debugDescription)   // "Optional(1)", ok
print(o)                    // "1", ok
debugPrint(o)               // "Optional(1)", ok
print("\(o)")               // "Optional(1)", why??


Nicola




More information about the swift-evolution mailing list