<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto"><div><span></span></div><div><div>Hey all,</div><div><br></div><div>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!</div><div><br></div><div>It's available here: <a href="https://gist.github.com/harlanhaskins/63b7343e7fe4e5f4c6cfbe9413a98fdd">https://gist.github.com/harlanhaskins/63b7343e7fe4e5f4c6cfbe9413a98fdd</a></div><div><br></div><div>I've posted the current draft below.</div><div><br></div><div>Thanks,</div><div>Harlan Haskins<br></div><div><br></div><div><h1 style="box-sizing: border-box; margin-right: 0px; margin-bottom: 16px; margin-left: 0px; line-height: 1.25; padding-bottom: 0.3em; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238); margin-top: 0px !important;"><font size="3"><span style="background-color: rgba(255, 255, 255, 0);">Disallow Optionals in String Interpolation Segments</span></font></h1><ul style="box-sizing: border-box; padding-left: 2em; margin-top: 0px; margin-bottom: 16px;"><li style="box-sizing: border-box;"><span style="background-color: rgba(255, 255, 255, 0);">Proposal: <a href="https://gist.github.com/harlanhaskins/NNNN-filename.md" style="box-sizing: border-box; -webkit-text-decoration-skip: objects; text-decoration: none;">SE-NNNN</a></span></li><li style="box-sizing: border-box; margin-top: 0.25em;"><span style="background-color: rgba(255, 255, 255, 0);">Authors: <a href="https://github.com/harlanhaskins" style="box-sizing: border-box; -webkit-text-decoration-skip: objects; text-decoration: none;">Harlan Haskins</a>, <a href="https://github.com/Julioacarrettoni" style="box-sizing: border-box; -webkit-text-decoration-skip: objects; text-decoration: none;">Julio Carrettoni</a>, <a href="https://github.com/CodaFi" style="box-sizing: border-box; -webkit-text-decoration-skip: objects; text-decoration: none;">Robert Widmann</a></span></li><li style="box-sizing: border-box; margin-top: 0.25em;"><span style="background-color: rgba(255, 255, 255, 0);">Review Manager: TBD</span></li><li style="box-sizing: border-box; margin-top: 0.25em;"><span style="background-color: rgba(255, 255, 255, 0);">Status: <span style="box-sizing: border-box; font-weight: 600;">Awaiting revie</span></span></li></ul><h2 style="box-sizing: border-box; margin-top: 24px; margin-bottom: 16px; line-height: 1.25; padding-bottom: 0.3em; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238);"><a id="user-content-introduction" class="anchor" href="https://gist.github.com/harlanhaskins/63b7343e7fe4e5f4c6cfbe9413a98fdd#introduction" aria-hidden="true" style="box-sizing: border-box; -webkit-text-decoration-skip: objects; text-decoration: none; float: left; padding-right: 4px; margin-left: -20px; line-height: 1;"><svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"></svg><font color="#000000" size="3"><span style="background-color: rgba(255, 255, 255, 0);"><path d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></span></font></a><font size="3"><span style="background-color: rgba(255, 255, 255, 0);">Introduction</span></font></h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;"><span style="background-color: rgba(255, 255, 255, 0);">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: <code style="box-sizing: border-box; padding: 0.2em 0px; margin: 0px; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;">Optional<T></code>. If a user puts an optional value into a string interpolation segment, it will insert either <code style="box-sizing: border-box; padding: 0.2em 0px; margin: 0px; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;">"Optional("value")"</code> or <code style="box-sizing: border-box; padding: 0.2em 0px; margin: 0px; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;">"nil"</code> 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.</span></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;"><span style="background-color: rgba(255, 255, 255, 0);">Swift-evolution thread: <a href="https://lists.swift.org/pipermail/swift-evolution/" style="box-sizing: border-box; -webkit-text-decoration-skip: objects; text-decoration: none;">Discussion thread topic for that proposal</a></span></p><h2 style="box-sizing: border-box; margin-top: 24px; margin-bottom: 16px; line-height: 1.25; padding-bottom: 0.3em; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238);"><a id="user-content-motivation" class="anchor" href="https://gist.github.com/harlanhaskins/63b7343e7fe4e5f4c6cfbe9413a98fdd#motivation" aria-hidden="true" style="box-sizing: border-box; -webkit-text-decoration-skip: objects; text-decoration: none; float: left; padding-right: 4px; margin-left: -20px; line-height: 1;"><svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"></svg><font color="#000000" size="3"><span style="background-color: rgba(255, 255, 255, 0);"><path d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></span></font></a><font size="3"><span style="background-color: rgba(255, 255, 255, 0);">Motivation</span></font></h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;"><span style="background-color: rgba(255, 255, 255, 0);"><em style="box-sizing: border-box;">The Swift Programming Language</em> 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: <code style="box-sizing: border-box; padding: 0.2em 0px; margin: 0px; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;">Optional</code>. The <code style="box-sizing: border-box; padding: 0.2em 0px; margin: 0px; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;">.none</code> case in particular is used to indicate the absence of a value. Moreover, its inclusion in interpolation segments leads to the dreaded <code style="box-sizing: border-box; padding: 0.2em 0px; margin: 0px; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;">"nil"</code> in output that is often fed to UI elements. Even barring that, interpolating a non-nil optional value yields <code style="box-sizing: border-box; padding: 0.2em 0px; margin: 0px; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;">"Optional("value")"</code>, a result that is not useful even in logged output.</span></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;"><span style="background-color: rgba(255, 255, 255, 0);">Given that the <code style="box-sizing: border-box; padding: 0.2em 0px; margin: 0px; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;">Optional</code> 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 <code style="box-sizing: border-box; padding: 0.2em 0px; margin: 0px; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;">Optional</code>'s debug description be an explicit act. This proposal now requires a warning when using an expression of <code style="box-sizing: border-box; padding: 0.2em 0px; margin: 0px; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;">Optional</code> type within a string interpolation segment.</span></p><h2 style="box-sizing: border-box; margin-top: 24px; margin-bottom: 16px; line-height: 1.25; padding-bottom: 0.3em; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238);"><a id="user-content-proposed-solution" class="anchor" href="https://gist.github.com/harlanhaskins/63b7343e7fe4e5f4c6cfbe9413a98fdd#proposed-solution" aria-hidden="true" style="box-sizing: border-box; -webkit-text-decoration-skip: objects; text-decoration: none; float: left; padding-right: 4px; margin-left: -20px; line-height: 1;"><svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"></svg><font color="#000000" size="3"><span style="background-color: rgba(255, 255, 255, 0);"><path d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></span></font></a><font size="3"><span style="background-color: rgba(255, 255, 255, 0);">Proposed solution</span></font></h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;"><span style="background-color: rgba(255, 255, 255, 0);">The user will be warned after attempting to use an expression with type <code style="box-sizing: border-box; padding: 0.2em 0px; margin: 0px; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;">Optional<T></code> in a string interpolation segment. They will then be offered a fixit suggesting they explicitly request the <code style="box-sizing: border-box; padding: 0.2em 0px; margin: 0px; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;">debugDescription</code> of the <code style="box-sizing: border-box; padding: 0.2em 0px; margin: 0px; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;">Optional</code> value instead.</span></p><h2 style="box-sizing: border-box; margin-top: 24px; margin-bottom: 16px; line-height: 1.25; padding-bottom: 0.3em; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238);"><a id="user-content-detailed-design" class="anchor" href="https://gist.github.com/harlanhaskins/63b7343e7fe4e5f4c6cfbe9413a98fdd#detailed-design" aria-hidden="true" style="box-sizing: border-box; -webkit-text-decoration-skip: objects; text-decoration: none; float: left; padding-right: 4px; margin-left: -20px; line-height: 1;"><svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"></svg><font color="#000000" size="3"><span style="background-color: rgba(255, 255, 255, 0);"><path d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></span></font></a><font size="3"><span style="background-color: rgba(255, 255, 255, 0);">Detailed design</span></font></h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;"><span style="background-color: rgba(255, 255, 255, 0);">Semantic analysis currently does not do much but guarantee the well-formedness of expressions in interpolation segments. These are then fed directly to <code style="box-sizing: border-box; padding: 0.2em 0px; margin: 0px; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;">String.init(stringInterpolationSegment:)</code> 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 <code style="box-sizing: border-box; padding: 0.2em 0px; margin: 0px; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;">Optional</code> and will offer a fixit in that case.</span></p><h2 style="box-sizing: border-box; margin-top: 24px; margin-bottom: 16px; line-height: 1.25; padding-bottom: 0.3em; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238);"><a id="user-content-impact-on-existing-code" class="anchor" href="https://gist.github.com/harlanhaskins/63b7343e7fe4e5f4c6cfbe9413a98fdd#impact-on-existing-code" aria-hidden="true" style="box-sizing: border-box; -webkit-text-decoration-skip: objects; text-decoration: none; float: left; padding-right: 4px; margin-left: -20px; line-height: 1;"><svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"></svg><font color="#000000" size="3"><span style="background-color: rgba(255, 255, 255, 0);"><path d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></span></font></a><font size="3"><span style="background-color: rgba(255, 255, 255, 0);">Impact on existing code</span></font></h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;"><span style="background-color: rgba(255, 255, 255, 0);">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.</span></p><h2 style="box-sizing: border-box; margin-top: 24px; margin-bottom: 16px; line-height: 1.25; padding-bottom: 0.3em; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238);"><a id="user-content-alternatives-considered" class="anchor" href="https://gist.github.com/harlanhaskins/63b7343e7fe4e5f4c6cfbe9413a98fdd#alternatives-considered" aria-hidden="true" style="box-sizing: border-box; -webkit-text-decoration-skip: objects; text-decoration: none; float: left; padding-right: 4px; margin-left: -20px; line-height: 1;"><svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"></svg><font color="#000000" size="3"><span style="background-color: rgba(255, 255, 255, 0);"><path d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></span></font></a><font size="3"><span style="background-color: rgba(255, 255, 255, 0);">Alternatives considered</span></font></h2><ul style="box-sizing: border-box; padding-left: 2em; margin-top: 0px; margin-bottom: 0px !important;"><li style="box-sizing: border-box;"><p style="box-sizing: border-box; margin-top: 16px; margin-bottom: 16px;"><span style="background-color: rgba(255, 255, 255, 0);">A fixit that suggests a default value be inserted would be entirely appropriate (following the style of the fixit introduced in <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0140-bridge-optional-to-nsnull.md" style="box-sizing: border-box; -webkit-text-decoration-skip: objects; text-decoration: none;">SE-0140</a>).</span></p></li><li style="box-sizing: border-box; margin-top: 0.25em;"><p style="box-sizing: border-box; margin-top: 16px; margin-bottom: 16px;"><span style="background-color: rgba(255, 255, 255, 0);">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.</span></p></li><li style="box-sizing: border-box; margin-top: 0.25em;"><p style="box-sizing: border-box; margin-top: 16px; margin-bottom: 16px;"><span style="background-color: rgba(255, 255, 255, 0);">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.</span></p></li></ul></div></div><div><br><br>Sent from my iPad</div></body></html>