Please see inline comments.

> On Jan 3, 2016, at 6:48 PM, Tyler Fleming Cloutier via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> Indeed both are reasonable but perhaps suboptimal. Consider the following 
> potential changes.
> 
> 
> // Assume this code is included for the below examples.
> func myThrowingFunc() throws -> String {
>     if drand48() < 0.5 {
>         throw NSError(domain: "", code: 0, userInfo: nil)
>     }
>     return ""
> }
> let str: String
> 
> 
> 
> The current syntax is very clear and straightforward. 
> 
> // Current syntax
> do {
>     str = try myThrowingFunc().stringByAppendingString("appended")
> } catch {
>     str = "Default"
>     print("caught!")
> }
> 
> There are two potential issues with it however. The first is that it is quite 
> verbose, and the second is that the try is actually marking two function 
> calls, one which throws and one which does not. In this fake example it’s 
> clear that myThrowingFunc throws, but in general try is not marking a single 
> point of failure.
> 
> 
> 
> One change might be to simply rename do blocks that can throw to try blocks. 
> 
> // Create try blocks which encapsulate potentially throwing code.
> try {
>     str = try myThrowingFunc().stringByAppendingString("appended")
> } catch {
>     str = "Default"
>     print("caught!")
> }
> 
> The motivation for doing this would be to clarify the difference between 
> blocks that can throw and blocks that can’t. For example, it’s helpful to not 
> have to scroll to the bottom of a long block to find catch, or scan through 
> all the lines to find the try keyword for a long block. You would be able to 
> see just from try that block was throwing. It would also be similar to many 
> other languages that use try to demarcate throwing blocks. The problems with 
> this are that it could be considered redundant, and is even more verbose (by 
> 1 character) than the current syntax. Furthermore, as with the current 
> syntax, try is not marking a single point of failure (and yet now we have to 
> try keywords).
> 
> 
> 
> Another change could be to rename do blocks that can throw to try blocks and 
> then not require explicit marking of try on throwing statements.
> 
> // Don't require explicit try marking within try blocks.
> try {
>     str = myThrowingFunc().stringByAppendingString("appended")
> } catch {
>     str = "Default"
>     print("caught!")
> }
> 
> This approach retains all of the benefits of the above change, including 
> familiarity for those coming from other languages. Also, it no longer 
> requires the redundant double try syntax. In this case try is not assumed to 
> be marking a single potentially failing call, but a group of them. 
> Unfortunately, this means that it might not be clear which function is the 
> function that can throw, in a block of code. However, this is already 
> somewhat the case for chained calls in the current syntax. Certainly, only 
> allowing this ambiguity for chained calls reduces the potential size of the 
> code that is unmarked, with functional paradigms long chains are not so 
> uncommon.
> 
> 
> 
> The final change that I have included above is really just a shortening of 
> syntax and could be applied to any of the above implementations to reduce 
> verbosity.
> 

I have included below*


> // Allow catch directly on try expression.
> let str = try myThrowingFunc().stringByAppendingString("appended”) catch {
>     str = "Default"
>     print("caught!")
> }
> 
> This also has the added benefit of not having to open up a new scope just to 
> catch an error. Additionally it’s very easy to refactor into a try? statement.
> 
> I’d really like to see how these changes might affect real world examples and 
> if I get some time, I will look for some and share them with the list. That 
> way we can really see what the effects of these changes would be within the 
> context of an actual use case.
> 
>> What would you think about a solution that just inverted the default.  
>> Rather than marking throwing expressions with `try` we could have a try 
>> block (with optional catch clauses) where non-throwing calls are marked with 
>> `do`.  The primary motivation for requiring `do` would be to prevent abuse 
>> of `try` blocks by making them awkward when there is a reasonable mix of 
>> throwing and non-throwing code.  A secondary benefit is that would still be 
>> clear what can throw and what can’t, although this is much less useful when 
>> most things can throw.
> 
> Also Matthew, I think this is an interesting idea. And I’d say you’ve hit on 
> the major problem with try blocks, potential excessive mixing of throwing and 
> non throwing code. You could perhaps enforce that try blocks must begin and 
> end with a potentially throwing statement to cut down on mixing, but people 
> might find that strange/confusing.
> 

You might even compromise to allow try blocks, but only in the case where every 
single statement can throw. This would at least solve the problem of many try 
statements in a row. This situation seems reasonably common. The following is 
from a cursory search of the SwiftPM source.

try popen(["git", "-C", dstdir, "init"])
try popen(["git", "-C", dstdir, "config", "user.email", "exam...@example.com"])
try popen(["git", "-C", dstdir, "config", "user.name", "Example Example"])
try popen(["git", "-C", dstdir, "add", "."])
try popen(["git", "-C", dstdir, "commit", "-m", "msg"])
try popen(["git", "-C", dstdir, "tag", tag])

That solves at least some of the problem.

Tyler

> Tyler
> 
>> On Jan 3, 2016, at 11:37 AM, Dave Abrahams <dabrah...@apple.com 
>> <mailto:dabrah...@apple.com>> wrote:
>> 
>>> 
>>> On Jan 3, 2016, at 10:21 AM, Matthew Johnson <matt...@anandabits.com 
>>> <mailto:matt...@anandabits.com>> wrote:
>>> 
>>>> 
>>>> On Jan 3, 2016, at 12:12 PM, Dave Abrahams via swift-evolution 
>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>>> 
>>>> 
>>>>> On Jan 2, 2016, at 2:23 PM, Tyler Cloutier <cloutierty...@aol.com 
>>>>> <mailto:cloutierty...@aol.com>> wrote:
>>>>> 
>>>>> Please see comments inline.
>>>>> 
>>>>>> On Dec 31, 2015, at 12:07 PM, Dave Abrahams via swift-evolution 
>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>>>>> 
>>>>>> 
>>>>>>>> On Dec 27, 2015, at 10:25 PM, Brent Royal-Gordon via swift-evolution 
>>>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>>>>>>> 
>>>>>>>> So “try” instead of “do”. If there is no catch, then just use braces 
>>>>>>>> without a keyword for a block. 
>>>>>>>> 
>>>>>>>> And use do-while instead of repeat-while.
>>>>>> 
>>>>>> +1
>>>>>> 
>>>>>>> 
>>>>>>> Do you also propose no longer marking calls to throwing functions with 
>>>>>>> `try`?
>>>>>> 
>>>>>> If try had both a single-statement/expression form as it does today, and 
>>>>>> a block form that makes it unnecessary to mark all the individual 
>>>>>> statements in the block, that would be an improvement.
>>>>>> 
>>>>>>> Have you read the "Error-Handling Rationale" document in the Swift 
>>>>>>> repository? If not, please do: 
>>>>>>> <https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst
>>>>>>>  
>>>>>>> <https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst>>
>>>>>>>  If so, please explain why you disagree with it.
>>>>>> 
>>>>>> There are large classes of programs where you can know you don’t care 
>>>>>> exactly where a failure happens, e.g. (most init functions, all pure 
>>>>>> functions, any function that doesn’t break invariants).  In these cases 
>>>>>> marking every statement or expression that can throw is just noise.  Try 
>>>>>> writing some serialization/deserialization code where the underlying 
>>>>>> stream can fail to see what I mean; you’ll have “try” everwhere, and it 
>>>>>> adds nothing to comprehensibility or maintainability.  Personally I 
>>>>>> would like to be able to label the function itself and not have to 
>>>>>> introuce a scope, but IMO being able to create “try blocks” would be a 
>>>>>> welcome addition and would even match the common case in blocks with 
>>>>>> catch clauses, where being aware of the exact line where the error was 
>>>>>> generated is typically not useful.
>>>>> 
>>>>> I had proposed something very similar to this around six months ago on 
>>>>> the swift-users list, but I think John McCall, had some (quite valid) 
>>>>> concerns with this.
>>>>> 
>>>>> Unfortunately I can't access those emails, but I think his concern was 
>>>>> that the purpose of try was to mark explicitly which statements throw and 
>>>>> this would defeat the purpose of that. People might just wrap large 
>>>>> blocks in try.
>>>> 
>>>> As much as I am loath to disagree with John on this, there’s an incorrect 
>>>> implicit assumption in that rationale, that forcing people to mark all 
>>>> throw points trains them to get error-handling correct.  What it does 
>>>> instead is to train them to think of all code uniformly instead of 
>>>> recognizing the places where a throw needs special attention (places where 
>>>> there are broken invariants). Eventually, as with warnings that have a 
>>>> high false-positive rate, when you see “try” in many places where it 
>>>> doesn’t help, you learn to ignore it altogether.
>>> 
>>> I agree that requiring this is not likely to result in improved error 
>>> handling and thus is not a strong argument in favor of it.
>>> 
>>> IMO the purpose of requiring “try” to be stated explicitly is that it 
>>> arguably makes code more readable.  It is immediately clear which functions 
>>> can throw and which cannot.  You don’t need to look up the signature of 
>>> every function called to determine this. My experience thus far has been 
>>> that I have really appreciated the requirement that throwing expressions be 
>>> explicitly marked. 
>> 
>> As a default it’s great.  Not having a way to opt out of individual marking 
>> for a whole block or function—because you know you’re not breaking any 
>> invariants, so which functions can throw is irrelevant, and not having a way 
>> for the compiler deduce these regions (e.g. known pure functions)—is the 
>> problem.  The recognizer code posted in an earlier message is a perfect 
>> example.  If there *was* some code where it was really important to notice 
>> failure points, you’d miss it. 
>> 
>> The key to getting error handling right is not being able to trace every 
>> possible control path—which is effectively impossible anyway— it’s 
>> understanding the relationship between scopes in your code and your 
>> program’s invariants.  
>> 
>>> I think positions on both sides of this are reasonable.
>> 
>> Absolutely.  Even reasonable positions can be sub-optimal though :-)
>> 
>>>> 
>>>>> 
>>>>> Another idea is to treat the block as an unnamed, no argument, no return 
>>>>> value, function that could throw. This solves the problem in a very 
>>>>> general way, and would retain the marking of all throwing functions with 
>>>>> try,
>>>> 
>>>> That marking, in itself, is the root problem.  Our syntax is the way it is 
>>>> primarily because "marking everywhere" was adopted as an explicit goal.
>>>> 
>>>>> but has the perhaps unfortunate syntax of allowing things like:
>>>>> 
>>>>> try {
>>>>> try myFunction()
>>>>> } catch {
>>>>> 
>>>>> }
>>>>> 
>>>>> Something like this could be shortened to a consistent theoretical inline 
>>>>> try catch syntax like:
>>>>> 
>>>>> try myFunction() catch {
>>>>> 
>>>>> }
>>>>> 
>>>>> Though, as John, pointed out at the time, this could still be added on 
>>>>> with the current syntax. Obviously treating a try like an unnamed 
>>>>> function would have different return semantics, so perhaps that's not the 
>>>>> right abstraction. (Although I recall a thread going on that is 
>>>>> considering allowing functions to retain return semantics of the outer 
>>>>> scope)
>>>>> 
>>>>> Tyler
>>>>> 
>>>>> 
>>>>>> 
>>>>>> -Dave
>>>>>> 
>>>>>> _______________________________________________
>>>>>> swift-evolution mailing list
>>>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>> 
>>>> -Dave
>>>> 
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> 
>> -Dave
> 
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to