<html><head><style>
body {
        font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
        padding:1em;
        margin:auto;
        background:#fefefe;
}
h1, h2, h3, h4, h5, h6 {
        font-weight: bold;
}
h1 {
        color: #000000;
        font-size: 28pt;
}
h2 {
        border-bottom: 1px solid #CCCCCC;
        color: #000000;
        font-size: 24px;
}
h3 {
        font-size: 18px;
}
h4 {
        font-size: 16px;
}
h5 {
        font-size: 14px;
}
h6 {
        color: #777777;
        background-color: inherit;
        font-size: 14px;
}
hr {
        height: 0.2em;
        border: 0;
        color: #CCCCCC;
        background-color: #CCCCCC;
display: inherit;
}
p, blockquote, ul, ol, dl, li, table, pre {
        margin: 15px 0;
}
a, a:visited {
        color: #4183C4;
        background-color: inherit;
        text-decoration: none;
}
#message {
        border-radius: 6px;
        border: 1px solid #ccc;
        display:block;
        width:100%;
        height:60px;
        margin:6px 0px;
}
button, #ws {
        font-size: 12 pt;
        padding: 4px 6px;
        border-radius: 5px;
        border: 1px solid #bbb;
        background-color: #eee;
}
code, pre, #ws, #message {
        font-family: Monaco;
        font-size: 10pt;
        border-radius: 3px;
        background-color: #F8F8F8;
        color: inherit;
}
code {
        border: 1px solid #EAEAEA;
        margin: 0 2px;
        padding: 0 5px;
}
pre {
        border: 1px solid #CCCCCC;
        overflow: auto;
        padding: 4px 8px;
}
pre > code {
        border: 0;
        margin: 0;
        padding: 0;
}
#ws { background-color: #f8f8f8; }
.bloop_markdown table {
border-collapse: collapse;
font-family: Helvetica, arial, freesans, clean, sans-serif;
color: rgb(51, 51, 51);
font-size: 15px; line-height: 25px;
padding: 0; }
.bloop_markdown table tr {
border-top: 1px solid #cccccc;
background-color: white;
margin: 0;
padding: 0; }
.bloop_markdown table tr:nth-child(2n) {
background-color: #f8f8f8; }
.bloop_markdown table tr th {
font-weight: bold;
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px; }
.bloop_markdown table tr td {
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px; }
.bloop_markdown table tr th :first-child, table tr td :first-child {
margin-top: 0; }
.bloop_markdown table tr th :last-child, table tr td :last-child {
margin-bottom: 0; }
.bloop_markdown blockquote{
border-left: 4px solid #dddddd;
padding: 0 15px;
color: #777777; }
blockquote > :first-child {
margin-top: 0; }
blockquote > :last-child {
margin-bottom: 0; }
code, pre, #ws, #message {
word-break: normal;
word-wrap: normal;
}
hr {
display: inherit;
}
.bloop_markdown :first-child {
-webkit-margin-before: 0;
}
code, pre, #ws, #message {
font-family: Menlo, Consolas, Liberation Mono, Courier, monospace;
}
.send { color:#77bb77; }
.server { color:#7799bb; }
.error { color:#AA0000; }</style></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class="bloop_markdown"><p>I propose that we do not add a suffix <code>?</code> to the arguments because it does not really signal that the whole function may return an optional value. This is especially not convenient in a nested scenario like mentioned by others in previous posts. Instead we should reuse the inifix <code>?</code> on functions, subscripts and initializer. This way we can signal that the function might fail if one of the provided optional arguments could not be unwrapped where the function expects a non-optional argument. If the return type was already an optional, we do not generate a nested optional, because there is no real reason for us to do so. All arguments can simply be passed to the function with an infix <code>?</code> in their current form. If all non-optional parameter types match with the argument types - optional parameter types can safely be ignored - we generate an error, thus we don’t need an infix <code>?</code>, otherwise the conversion happens implicitly. This approach removes the need for explicit argument annotations, which is IMHO boilerplate in first place. It also provides a hint to the reader that some of the passed arguments should be unwrapped to succeed. In every chase the <code>?</code> is always close to the parameter list <code>?(_:_:...)</code>.</p>
<p>Here are some simple examples to consider: </p>
<pre><code class="swift">func first(one: A, two: B, three: C?) -> D { ... }
func second(one: A, two: B, three: C?) -> D? { ... }
</code></pre>
<p>————————————————————————————————————————————————</p>
<pre><code class="swift">// error when adding infix `?` if all arguments match the types
first?(one: a, two: b, three: c) // c can be both optional and non-optional
</code></pre>
<p>————————————————————————————————————————————————</p>
<pre><code class="swift">// no need for infix `?`
first(one: a, two: b, three: optionalC) // returns `D`
</code></pre>
<p>————————————————————————————————————————————————</p>
<pre><code class="swift">// sugared
first?(one: optionalA, two: b, three: c) // returns `D?` if succeed
// desugared
{ () -> D? in
if let someA = optionalA {
return first(one: someA, two: b, three: c)
} else {
return nil
}
}()
</code></pre>
<p>————————————————————————————————————————————————</p>
<pre><code class="swift">// sugared
first?(one: optionalA, two: optionalB, three: optionalC) // returns `D?` if succeed
// desugared
{ () -> D? in
if let someA = optionalA, let someB = optionalB {
return first(one: someA, two: someB, three: optionalC)
} else {
return nil
}
}()
</code></pre>
<p>————————————————————————————————————————————————</p>
<pre><code class="swift">// error when adding infix `?` if all arguments match the types
second?(one: a, two: b, three: c) // c can be both optional and non-optional
</code></pre>
<p>————————————————————————————————————————————————</p>
<pre><code class="swift">// no need for infix `?`
second(one: a, two: b, three: optionalC) // returns `D?`
</code></pre>
<p>————————————————————————————————————————————————</p>
<pre><code class="swift">// sugared
second?(one: optionalA, two: b, three: c) // returns `D?` if succeed - no need for nested optionals!
// desugared
{ () -> D? in
if let someA = optionalA {
return second(one: someA, two: b, three: c)
} else {
return nil
}
}()
</code></pre>
<p>————————————————————————————————————————————————</p>
<pre><code class="swift">// sugared
second?(one: optionalA, two: optionalB, three: optionalC) // returns `D?` if succeed - no need for nested optionals!
// desugared
{ () -> D? in
if let someA = optionalA, let someB = optionalB {
return second(one: someA, two: someB, three: optionalC)
} else {
return nil
}
}()
</code></pre>
<p>Please note that this can only work with non-operator functions and initializers. Operator functions cannot be referenced as normal functions yet and optional subscripts would require a source breaking change to align with that behaviour.</p>
<p>If I missed something where <code>optional func</code> from Objective-C results into incompatibility with this approach, please fell free to correct me. From my point of view I don’t see how this additional behaviour could break <code>optional func</code>.</p>
<p></p></div><div class="bloop_original_html"><style>body{font-family:Helvetica,Arial;font-size:13px}</style><div id="bloop_customfont" style="font-family:Helvetica,Arial;font-size:13px; color: rgba(0,0,0,1.0); margin: 0px; line-height: auto;"><br></div> <br> <div id="bloop_sign_1513106396093748992" class="bloop_sign"></div> <br><p class="airmail_on">Am 12. Dezember 2017 um 19:47:25, Jared Khan via swift-evolution (<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>) schrieb:</p> <blockquote type="cite" class="clean_bq"><span><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div></div><div>
<title></title>
<div class="">Even this small example I think this is still a
little fiddly. If we add another required parameter to the Markdown
initializer:</div>
<div class="">let readme = String(contentsOfFile:
“README.md”).flatMap { Markdown(string: $0, flavor: .github)
}</div>
<div class="">this starts to feel a little inside-out and crufty to
me. <br class="">
<div><br class="">
<blockquote type="cite" class="">
<div class="">On 12 Dec 2017, at 05:54, Félix Cloutier <<a href="mailto:felixcloutier@icloud.com" class="">felixcloutier@icloud.com</a>> wrote:</div>
<br class="Apple-interchange-newline">
<div class="">
<div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">You talk about flatMap without giving an example. The
readme isn't that bad with it, IMO:<br class="">
<div class=""><br class=""></div>
<div class="">
<div class="">if let readme = String(contentsOfFile:
"README.md").flatMap(Markdown) {</div>
<div class="">// use contents here</div>
<div class="">}</div>
<div class=""><br class=""></div>
<div class="">That doesn't work when you need multiple optional
parameters. In my own experience, that hasn't been a huge problem,
though.</div>
<div class=""><br class=""></div>
<div class="">Félix</div>
<div class=""><br class="">
<blockquote type="cite" class="">
<div class="">Le 11 déc. 2017 à 08:30, Jared Khan via
swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> a écrit :</div>
<br class="Apple-interchange-newline">
<div class="">
<div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">
<p class="">Hi all,</p>
<p class="">I'd like to propose a syntax addition that acts to ease
some things that I believe should fall under the umbrella of
'optional chaining'. Optional chaining allows us to access the
properties of an optional value and return nil if any link in that
chain breaks. I propose we introduce syntax to allow similar
chaining when passing optional valued parameters to functions that
expect that parameter to be non-optional.</p>
<p class="">The example below is taken from a project I'm working
on at the moment:</p>
<pre class=""><br class=""></pre>
<blockquote class="" style="margin: 0px 0px 0px 40px; border: none; padding: 0px;">
<pre class=""><code class="">// Current
let readme: Markdown?
if let rawMarkdown = String(contentsOfFile: "README.md") {
readme = Markdown(string: rawMarkdown)
} else {
readme = nil
}</code></pre></blockquote>
<p class="">In this example we want to perform an operation, the
initialisation of a 'Markdown' type, with our raw text if it exists
and get nil otherwise. This is rather verbose</p>
<p class="">I propose the following syntax for an alternative:</p>
<div class=""><br class=""></div>
<blockquote class="" style="margin: 0px 0px 0px 40px; border: none; padding: 0px;">
<pre class=""><code class="">// Proposed alternative
let readme = Markdown(string: String(contentsOfFile: "README.md")?)</code></pre>
<pre class=""><code class=""><br class=""></code></pre></blockquote>
<p class="">The <code class="">?</code> is familiar in
its use for optional chaining.</p>
<p class="">This would act like syntactic sugar for
the <code class="">flatMap</code> method
on <code class="">Optional</code>. For example:</p>
<div class="">(where `john` is of a `Person` type with a property
`address: Address?`)</div>
<blockquote class="" style="margin: 0px 0px 0px 40px; border: none; padding: 0px;">
<pre class=""><code class="">// func getZipCode(fromAddress address: Address) -> ZipCode</code></pre>
<pre class=""><code class="">getZipCode(fromAddress: john.address?)
// Would be equivalent to…
john.address.flatMap {
getZipCode($0)
}</code></pre></blockquote>
<p class="">An example with multiple parameters:</p>
<blockquote class="" style="margin: 0px 0px 0px 40px; border: none; padding: 0px;">
<pre class=""><code class="">// func getPostageEstimate(source: Address, destination: Address, weight: Double) -> Int</code></pre>
<pre class=""><code class="">getPostageEstimate(source: john.address?, destination: alice.address?, weight: 2.0)
// Equivalent to
john.address.flatMap { freshVar1 in
alice.address.flatMap { freshVar2 in
getPostageEstimate(source: freshVar1, destination: freshVar2, weight: 2.0)
}
}
// Or equally:
{
guard let freshVar1 = john.address,
let freshVar2 = alice.address else {
return nil
}
return getPostageEstimate(source: freshVar1, destination: freshVar2, weight: 2.0)
}()</code></pre></blockquote>
<p class="">This would only be allowed when the parameter doesn’t
already accept Optionals and when the chained value is in fact an
optional. We’d want to consider emitting at least the following
errors/warnings in the given scenarios:</p>
<blockquote class="" style="margin: 0px 0px 0px 40px; border: none; padding: 0px;">
<pre class=""><code class="">
let result = myFunc(3?)
// error: cannot use optional chaining on non-optional value of type 'Int'
// func myFunc2(x: String?) -> String
let result = myFunc2(x: john.address?)
// error: cannot use optional argument chaining on argument of optional type</code></pre>
<pre class=""><code class="">let result = myFunc(nil?)
// warning: optional argument chaining with nil literal always results in nil</code></pre>
<pre class=""><code class=""><br class=""></code></pre></blockquote>
<p class="">Seeking your thoughts on this idea, the specific
syntax, and more use case examples.</p>
<p class="">Best,</p>
<p class="">Jared</p>
</div>
_______________________________________________<br class="">
swift-evolution mailing list<br class="">
<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br class="">
</div>
</blockquote>
</div>
<br class=""></div>
</div>
</div>
</blockquote>
</div>
<br class=""></div>
_______________________________________________<br>swift-evolution mailing list<br>swift-evolution@swift.org<br>https://lists.swift.org/mailman/listinfo/swift-evolution<br></div></div></span></blockquote></div><div class="bloop_markdown"><p></p></div></body></html>