<div dir="ltr"><div>On Mon, Aug 21, 2017 at 4:09 PM, Karim Nassar via swift-evolution <span dir="ltr">&lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evoluti<wbr>on@swift.org</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word">Thought about it in more depth, and I’m now firmly in the camp of: ‘throws’/‘try&#39; and ‘async’/‘await&#39; should be orthogonal features. I think the slight call-site reduction in typed characters (&#39;try await’ vs ‘await’) is heavily outweighed by the loss of clarity on all the edge cases.</div></blockquote></div><div><br></div>My concern is less for&#39; <span style="font-size:12.800000190734863px">‘throws’/‘async&#39; in declarations and ‘try’/‘await&#39; at call sites ( I can live with both for clarity and explicitness) than it is for the enclosing &#39;beginAsync/do&#39;. </span><span style="font-size:12.800000190734863px">Johnathan Hull made a similar point on <a href="https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170821/039142.html">another thread</a>. </span><div><span style="font-size:12.800000190734863px"><br></span></div><div><span style="font-size:12.800000190734863px">The principle at work, that aligns with the concurrency manifesto &#39;design&#39; section, is that it should not be the case that handling errors is more onerous than ignoring or discarding them. Nor </span><span style="font-size:12.800000190734863px">should handling errors produce ugly code. </span><br></div><div><span style="font-size:12.800000190734863px"><br></span></div><div><span style="font-size:12.800000190734863px">If handling errors requires nesting a do/try/catch in a beginAsync block every time, then code that ignored errors will always look cleaner than responsible code that handles them. Not handling errors will be the default. If there are specific recoverable errors, like moving a file from a download task only to find something else already moved it there, now there are multiple nested do/try/catch.</span></div><div><span style="font-size:12.800000190734863px"><br></span></div><div><span style="font-size:12.800000190734863px">(I&#39;m not sure what the reasoning is that most users won&#39;t have to interact with primitive  `beginAsync`. That seems like it would be the starting point for every use of any asynchronous function, especially in iOS development, as seen in the IBAction example)</span></div><div><span style="font-size:12.800000190734863px"><br></span></div><div><span style="font-size:12.800000190734863px">Leaving explicit throws/async in declarations and try/await at call sites, a narrower modification would be:</span></div><div><span style="font-size:12.800000190734863px"><br></span></div><div><span style="font-size:12.800000190734863px">1.  Make every `beginAsync` coroutine start also a `do` block. The `catch` block would be necessary only if something throws-- currently a `do` block can be used this way, so it matches nicely. </span></div><div><span style="font-size:12.800000190734863px"><br></span></div><div><span style="font-size:12.800000190734863px">(This is the same as Johnathan Hull noted on the thread, that if a goal is to eliminate the pyramid of doom, requiring two levels of indentation to do anything isn&#39;t a clear win.) </span></div><div><div><br></div><div><span style="font-size:12.800000190734863px">2. The two syspendAsync calls should be only the one: </span></div><div>func suspendAsync&lt;T&gt;(</div><div>    _ body: (_ continuation: @escaping (T) -&gt; (),</div><div>    _ error: @escaping (Error) -&gt; ()) -&gt; ()</div><div>) async throws -&gt; T</div><div><span style="font-size:12.800000190734863px"><br></span></div><div><span style="font-size:12.800000190734863px">We can assume the programmers using this function basically know what they&#39;re doing (not the same as being unwilling to cut corners, so make them cut corners explicitly and visibly). If a user of this function knows that no errors are possible, then then it should be called with try! and the error closure ignored, presumably by replacing it with `_`. To force `try! suspendAsync` and then call the error closure would be a programmer error. It would also be a programmer error to keep the throwing function and then not call the error block, but they can do that with the API as proposed--</span><span style="font-size:12.800000190734863px">that&#39;s just harder with the API as proposed because the single-continuation suspend method makes it easy to write code that ignores errors.   </span></div><div><span style="font-size:12.800000190734863px"><br></span></div><div><span style="font-size:12.800000190734863px">We are talking not only about a language change, but making all Swift programmers adopt a new methodology. It&#39;s an opportunity to build new habits, </span><span style="font-size:12.800000190734863px">as noted in the manifesto,</span><span style="font-size:12.800000190734863px"> by placing the right thing to do at hand. Two years ago, moving from passing a pointer to an NSError </span><span style="font-size:12.800000190734863px">to do-try-catch--and truly no one ever used that pattern outside Cocoa frameworks, a crisis-level problem in error handling-- it was a huge obvious win. It made dealing with errors so much better. Completion blocks are not at the level of NSError-pointer-pointer level crisis, but regardless this should have similar improvement when doing the right thing. </span></div><div><span style="font-size:12.800000190734863px"><br></span></div><div><div><span style="font-size:12.800000190734863px">(And my opinion on try?/try!: `try?` I seldom see used, find it be an anti-pattern of ignoring errors instead of explicitly recovering; I actually wish it wasn&#39;t in the language, but guess some people find it useful. `try!` is necessary and useful for cases where the compiler can&#39;t guarantee success and the programmer is willing to assert it&#39;s not going to fail, and `!` marks those points in code nicely, matching the optional syntax.) </span></div></div><div><span style="font-size:12.800000190734863px"><br></span></div><div><span style="font-size:12.800000190734863px">About potential await? and await!: If we kept call sites `try await` (or `await try`?) then the `try?/try!` semantics would behave the same, another argument for that. I assume `await!` would have the current queue block until the function returns. </span></div><div><br></div><div><span style="font-size:12.800000190734863px">The stronger need is for better recognition that often async functions will _sometimes_ have their values or errors immediately, if cached, or known to be impossible to get. In promise/futures, this is the equivalent of creating a future already fulfilled with a value or error. This might just be an educational point, though. </span></div><div><span style="font-size:12.800000190734863px"><br></span></div><div><span style="font-size:12.800000190734863px">Maybe the use of `await?` could be this, checking if the value exists already-- fulfill the value if it can without suspending, but return nil if not--throwing if known error, unless try? also so annotated. </span></div><div><span style="font-size:12.800000190734863px"><br></span></div><div><span style="font-size:12.800000190734863px">Really interesting and insightful comments on this thread, look forward to seeing how this further evolves. </span></div><div><span style="font-size:12.800000190734863px"><br></span></div><div><span style="font-size:12.800000190734863px">Mike Sanderson </span></div><div><span style="font-size:12.800000190734863px"><br></span></div><div><span style="font-size:12.800000190734863px">  </span></div><div><span style="font-size:12.800000190734863px"> </span></div><div><br></div><div><span style="font-size:12.800000190734863px"><br></span></div><div><span style="font-size:12.800000190734863px"><br></span></div></div><div class="gmail_extra"><br><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word"><div><br></div><div>—Karim</div><div><div class="gmail-m_-295617782482181838gmail-m_5126071872134790661gmail-m_3479891020191261014h5"><br><div><blockquote type="cite"><div>On Aug 21, 2017, at 1:56 PM, John McCall &lt;<a href="mailto:rjmccall@apple.com" target="_blank">rjmccall@apple.com</a>&gt; wrote:</div><br class="gmail-m_-295617782482181838gmail-m_5126071872134790661gmail-m_3479891020191261014m_-1183538104196964220Apple-interchange-newline"><div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><blockquote type="cite"><div><br class="gmail-m_-295617782482181838gmail-m_5126071872134790661gmail-m_3479891020191261014m_-1183538104196964220Apple-interchange-newline">On Aug 20, 2017, at 3:56 PM, Yuta Koshizawa &lt;<a href="mailto:koher@koherent.org" target="_blank">koher@koherent.org</a>&gt; wrote:</div><br class="gmail-m_-295617782482181838gmail-m_5126071872134790661gmail-m_3479891020191261014m_-1183538104196964220Apple-interchange-newline"><div><div dir="ltr">2017-08-21 2:20 GMT+09:00 John McCall via swift-evolution <span dir="ltr">&lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evoluti<wbr>on@swift.org</a>&gt;</span>:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word"><span class="gmail-m_-295617782482181838gmail-m_5126071872134790661gmail-m_3479891020191261014m_-1183538104196964220gmail-"><blockquote type="cite"><div>On Aug 19, 2017, at 7:17 PM, Chris Lattner via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt; wrote:</div><div><div style="word-wrap:break-word"><blockquote type="cite"><div>On Aug 19, 2017, at 8:14 AM, Karim Nassar via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt; wrote:</div><br class="gmail-m_-295617782482181838gmail-m_5126071872134790661gmail-m_3479891020191261014m_-1183538104196964220gmail-m_5864697011298285341Apple-interchange-newline"><div><div style="word-wrap:break-word"><div>This looks fantastic. Can’t wait (heh) for async/await to land, and the Actors pattern looks really compelling.</div><div><br></div><div>One thought that occurred to me reading through the section of the &quot;async/await&quot; proposal on whether async implies throws:</div><div><br></div><div>If ‘async&#39; implies ‘throws&#39; and therefore ‘await&#39; implies ‘try’, if we want to suppress the catch block with ?/!, does that mean we do it on the ‘await’ ? </div></div></div></blockquote><blockquote type="cite"><div style="word-wrap:break-word"><div><br></div><div><font face="Menlo">guard let foo = await? getAFoo() else {  …  }</font></div></div></blockquote><div><br></div><div>Interesting question, I’d lean towards “no, we don’t want await? and await!”.  My sense is that the try? and try! forms are only occasionally used, and await? implies heavily that the optional behavior has something to do with the async, not with the try.  I think it would be ok to have to write “try? await foo()” in the case that you’d want the thrown error to turn into an optional.  That would be nice and explicit.</div></div></div></blockquote><div><br></div></span>try? and try! are quite common from what I&#39;ve seen.</div></blockquote><div class="gmail_extra"><br></div><div class="gmail_extra">As analogous to `throws` and `try`, I think we have an option that `await!` means blocking.</div><div class="gmail_extra"><br></div><div class="gmail_extra">First, if we introduce something like `do/catch` for `async/await`, I think it should be for blocking. For example:</div><div class="gmail_extra"><br></div><div class="gmail_extra">```</div><div class="gmail_extra">do {</div><div class="gmail_extra">  return await foo()</div><div class="gmail_extra">} block</div><div class="gmail_extra">```</div><div class="gmail_extra"><br></div><div class="gmail_extra">It is consistent with `do/try/catch` because it should allow to return a value from inside `do` blocks for an analogy of `throws/try`.</div><div class="gmail_extra"><br></div><div class="gmail_extra">```</div><div class="gmail_extra">// `throws/try`</div><div class="gmail_extra">func foo() -&gt; Int {</div><div class="gmail_extra">  do {</div><div class="gmail_extra">    return try bar()</div><div class="gmail_extra">  } catch {</div><div class="gmail_extra">    ...</div><div class="gmail_extra">  }<br></div><div class="gmail_extra">}</div><div class="gmail_extra"><br></div><div class="gmail_extra">// `async/await`</div><div class="gmail_extra">func foo() -&gt; Int {</div><div class="gmail_extra">  do {</div><div class="gmail_extra">    return await bar()</div><div class="gmail_extra">  } block</div><div class="gmail_extra">}</div><div class="gmail_extra">```</div><div class="gmail_extra"><br></div><div class="gmail_extra">And `try!` is similar to `do/try/catch`.</div><div class="gmail_extra"><br></div><div class="gmail_extra">```</div><div class="gmail_extra">// `try!`</div><div class="gmail_extra">let x = try! foo()</div><div class="gmail_extra"><div class="gmail_extra">// uses `x` here</div><div class="gmail_extra"><br></div><div class="gmail_extra">// `do/try/catch`</div><div class="gmail_extra">do {<br></div></div><div class="gmail_extra">  let x = try foo()</div><div class="gmail_extra">  // uses `x` here</div><div class="gmail_extra">} catch {<br>  fatalError()<br>}</div><div class="gmail_extra">```</div><div class="gmail_extra"><br></div><div class="gmail_extra">If `try!` is a sugar of `do/try/catch`, it also seems natural that `await!` is a sugar of `do/await/block`. However, currently all `!` in Swift are related to a logic failure. So I think using `!` for blocking is not so natural in point of view of symbology.</div><div class="gmail_extra"><br></div><div class="gmail_extra">Anyway, I think it is valuable to think about what `do` blocks for `async/await` mean. It is also interesting that thinking about combinations of `catch` and `block` for `async throws` functions: e.g. If only `block`, the enclosing function should be `throws`.</div></div></div></blockquote><div><br></div>Personally, I think these sources of confusion are a good reason to keep the feature separate.</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">The idea of using await! to block a thread is interesting but, as you say, does not fit with the general meaning of ! for logic errors.  I think it&#39;s fine to just have an API to block waiting for an async operation, and we can choose the name carefully to call out the danger of deadlocks.</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">John.</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br><blockquote type="cite"><div><div dir="ltr"><div class="gmail_extra"><br></div><div class="gmail_extra">That aside, I think `try!` is not so occasional and is so important. Static typing has limitations. For example, even if we has a text field which allows to input only numbers, we still get an input value as a string and parsing it may fail on its type though it actually never fails. If we did not have easy ways to convert such a simple domain error or a recoverable error to a logic failure, people would start ignoring them as we has seen in Java by `catch(Exception e) {}`. Now we have `JSONDecoder` and we will see much more `try!` for bundled JSON files in apps or generated JSONs by code, for which decoding fails as a logic failure.</div><div class="gmail_extra"><br></div><div class="gmail_extra"><div><div class="gmail-m_-295617782482181838gmail-m_5126071872134790661gmail-m_3479891020191261014m_-1183538104196964220gmail_signature">--<br>Yuta</div></div></div></div></div></blockquote></div></div></blockquote></div><br></div></div></div><br>______________________________<wbr>_________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailma<wbr>n/listinfo/swift-evolution</a><br>
<br></blockquote></div><br></div></div>