Re: [swift-evolution] await keyword "scope"

2017-09-19 Thread Brent Royal-Gordon via swift-evolution
> On Sep 18, 2017, at 9:47 AM, Adam Kemp  wrote:
> 
>> Would it be possible to actually fix this? That is, make the code covered by 
>> the `await` evaluate synchronous subexpressions first, such that the code 
>> sample above is equivalent to this?
>> 
>>  @IBAction func buttonDidClick(sender:AnyObject) {
>>  beginAsync {
>>  let $temp1 = self.resizeSwitch.isOn
>>  let $temp2 = await downloadImage()
>>  let image = await processImage($temp2, resize: $temp1)
>>  displayImage(image)
>>  }
>>  }
> 
> That violates the defined order of evaluation for function arguments.

I understand that, but this order of evaluation was designed before we had 
`await`. I'm suggesting that, now that we do, we should change the order so 
that synchronous subexpressions are evaluated before asynchronous ones.

> You could also write code in which the (async) first argument function call 
> has side effects that alter the result of the second argument expression. I’m 
> not saying that’s good code, but it’s possible, and the language defines the 
> order of evaluation so that code like that will have a predictable behavior.


You could write something like that, but as you say, that's not necessarily 
good code. Honestly, I don't think you can write good code that depends on one 
part of an expression being evaluated after an async call in another part of 
the expression completes.

-- 
Brent Royal-Gordon
Architechies

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


Re: [swift-evolution] await keyword "scope"

2017-09-18 Thread Adam Kemp via swift-evolution

> On Sep 15, 2017, at 4:50 AM, Brent Royal-Gordon  
> wrote:
> 
>> On Sep 12, 2017, at 12:48 PM, Adam Kemp via swift-evolution 
>> > wrote:
>> 
>> @IBAction func buttonDidClick(sender:AnyObject) {
>> beginAsync {
>> let image = await processImage(downloadImage(), resize: 
>> self.resizeSwitch.isOn)
>> displayImage(image)
>> }
>> }
> 
> Would it be possible to actually fix this? That is, make the code covered by 
> the `await` evaluate synchronous subexpressions first, such that the code 
> sample above is equivalent to this?
> 
>   @IBAction func buttonDidClick(sender:AnyObject) {
>   beginAsync {
>   let $temp1 = self.resizeSwitch.isOn
>   let $temp2 = await downloadImage()
>   let image = await processImage($temp2, resize: $temp1)
>   displayImage(image)
>   }
>   }

That violates the defined order of evaluation for function arguments. You could 
also write code in which the (async) first argument function call has side 
effects that alter the result of the second argument expression. I’m not saying 
that’s good code, but it’s possible, and the language defines the order of 
evaluation so that code like that will have a predictable behavior.___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] await keyword "scope"

2017-09-15 Thread Brent Royal-Gordon via swift-evolution
> On Sep 12, 2017, at 12:48 PM, Adam Kemp via swift-evolution 
>  wrote:
> 
> @IBAction func buttonDidClick(sender:AnyObject) {
> beginAsync {
> let image = await processImage(downloadImage(), resize: 
> self.resizeSwitch.isOn)
> displayImage(image)
> }
> }

Would it be possible to actually fix this? That is, make the code covered by 
the `await` evaluate synchronous subexpressions first, such that the code 
sample above is equivalent to this?

@IBAction func buttonDidClick(sender:AnyObject) {
beginAsync {
let $temp1 = self.resizeSwitch.isOn
let $temp2 = await downloadImage()
let image = await processImage($temp2, resize: $temp1)
displayImage(image)
}
}

-- 
Brent Royal-Gordon
Architechies

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


Re: [swift-evolution] await keyword "scope"

2017-09-13 Thread Thorsten Seitz via swift-evolution
Totally agree with Adam.

> Am 13.09.2017 um 00:39 schrieb Adam Kemp via swift-evolution 
> :
> 
>> On Sep 12, 2017, at 2:40 PM, Wallacy  wrote:
>> 
>> "No, the order of execution is well-defined. The problem isn’t in which 
>> order the arguments are executed. The problem is when that evaluation 
>> happens with respect to when we return to the run loop. That’s an entirely 
>> new problem with async/await. In Swift today it is not possible to write 
>> code that returns to the run loop in between evaluation of arguments."
>> 
>> I understand this, however if you reverse the evaluate order, in this 
>> particular problem, there's no bug.
> 
> I’m not sure I understand what you mean by this. We can’t reverse the 
> evaluation order. It’s defined by the language.
> 
> Maybe you meant if the arguments themselves were ordered the other way. That 
> would “fix” the bug, but the point of the example isn’t “show me how to fix 
> this bug”. The point is that “given only this one line of code you can’t tell 
> me whether the switch’s property is read before returning to the run loop or 
> after”. There’s not enough information in that line of code itself to 
> determine whether that’s a bug.
> 
> On the other hand, with an extra “await” I could tell you without looking at 
> any other code that there is a bug on that line. It’s a difference in clarity.

I, too, think that this is important (and different from the case with `try`).


> Will it prevent all bugs like this from happening? Of course not. That’s not 
> the point. The point is that by making the code clearer we can make it 
> actually possible to look at the code and reason about it in isolation. We 
> still have to understand the idea that the switch should be read before 
> returning to the run loop, and we still have to actually write the code such 
> that it does that. But the added clarity of an explicit await gives us enough 
> information to know where that read needs to be. That’s valuable not only to 
> the person writing the code, but to someone reviewing the code as well.
> 
>> Actually, I think several await on the same line will only make the 
>> debugging process more difficult.
> 
> How so? It would literally compile to the same code so I’m not sure how the 
> debugger would be affected.
> 
>> I really do not know if this is a really common problem. In practice I 
>> believe that anyone who is observing the code will know, unless it is the 
>> first time that he is doing maintenance. And if this is the case, another 
>> await probably will not help too.
> 
> Again, as someone who has actually used async/await in practice in shipping 
> code I am telling you that this is a common problem, and it does benefit 
> someone reading that code. This isn’t speculation. This is experience.
> 
>> A simple break point in this line will help!
> 
> I shouldn’t have to run code in a debugger to be able to spot this bug. 

Totally agree with that, especially in a language like Swift with a strong type 
system.

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


Re: [swift-evolution] await keyword "scope"

2017-09-12 Thread Adam Kemp via swift-evolution


> On Sep 12, 2017, at 2:40 PM, Wallacy  wrote:
> 
> "No, the order of execution is well-defined. The problem isn’t in which order 
> the arguments are executed. The problem is when that evaluation happens with 
> respect to when we return to the run loop. That’s an entirely new problem 
> with async/await. In Swift today it is not possible to write code that 
> returns to the run loop in between evaluation of arguments."
> 
> I understand this, however if you reverse the evaluate order, in this 
> particular problem, there's no bug.

I’m not sure I understand what you mean by this. We can’t reverse the 
evaluation order. It’s defined by the language.

Maybe you meant if the arguments themselves were ordered the other way. That 
would “fix” the bug, but the point of the example isn’t “show me how to fix 
this bug”. The point is that “given only this one line of code you can’t tell 
me whether the switch’s property is read before returning to the run loop or 
after”. There’s not enough information in that line of code itself to determine 
whether that’s a bug.

On the other hand, with an extra “await” I could tell you without looking at 
any other code that there is a bug on that line. It’s a difference in clarity.

Will it prevent all bugs like this from happening? Of course not. That’s not 
the point. The point is that by making the code clearer we can make it actually 
possible to look at the code and reason about it in isolation. We still have to 
understand the idea that the switch should be read before returning to the run 
loop, and we still have to actually write the code such that it does that. But 
the added clarity of an explicit await gives us enough information to know 
where that read needs to be. That’s valuable not only to the person writing the 
code, but to someone reviewing the code as well.

> Actually, I think several await on the same line will only make the debugging 
> process more difficult.

How so? It would literally compile to the same code so I’m not sure how the 
debugger would be affected.

> I really do not know if this is a really common problem. In practice I 
> believe that anyone who is observing the code will know, unless it is the 
> first time that he is doing maintenance. And if this is the case, another 
> await probably will not help too.

Again, as someone who has actually used async/await in practice in shipping 
code I am telling you that this is a common problem, and it does benefit 
someone reading that code. This isn’t speculation. This is experience.

> A simple break point in this line will help!

I shouldn’t have to run code in a debugger to be able to spot this bug. ___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] await keyword "scope"

2017-09-12 Thread Wallacy via swift-evolution
"No, the order of execution is well-defined. The problem isn’t in which
order the arguments are executed. The problem is when that evaluation
happens with respect to when we return to the run loop. That’s an entirely
new problem with async/await. In Swift today it is not possible to write
code that returns to the run loop in between evaluation of arguments."

I understand this, however if you reverse the evaluate order, in this
particular problem, there's no bug.

My point is: When order is important, it will not be a new "await" inline
that will solve. The developer will look at the "await" and may still not
realize that the root of the bug was because `resizeSwitchIsOn` was
obtained before. Another `await` inline will not make clear this problem,
maybe will for you but not for everybody.

Actually, I think several await on the same line will only make the
debugging process more difficult.

There is no proof that this will help the developer for cases like that.
Most likely it will put the break point inside processImage and see what
values were loaded. And even at call side the debugger will tell the
expected values.

"True. There are many ways to write bugs. The question is whether the
language makes those bugs easier to spot or does it obfuscate them? Making
await explicit every time makes this bug easier to see. If I were doing a
code review I could spot that bug. Without the explicit await it would be
much harder to spot by just reading the code. It’s not clear whether it’s
even a bug unless you already know that “processImage” is an async function.
"

I really do not know if this is a really common problem. In practice I
believe that anyone who is observing the code will know, unless it is the
first time that he is doing maintenance. And if this is the case, another
await probably will not help too.

A simple break point in this line will help!

"As I’ve mentioned before, the proposal is a proposal. This is the
discussion of the proposal. I think it’s reasonable for us to disagree with
the proposal, especially if we can explain why we think it should change."

You only pointed  the need to returning to the right queue. No disagreement
here. You can suggest other way of course. I also don't like this style too.



Em ter, 12 de set de 2017 às 17:53, Adam Kemp 
escreveu:

>
> On Sep 12, 2017, at 1:00 PM, Wallacy  wrote:
>
> So, the problem is a predefined order to evaluate the values, not a
> "second" await.
>
>
> No, the order of execution is well-defined. The problem isn’t in which
> order the arguments are executed. The problem is when that evaluation
> happens with respect to when we return to the run loop. That’s an entirely
> new problem with async/await. In Swift today it is not possible to write
> code that returns to the run loop in between evaluation of arguments.
>
> Like you said, the person can write the wrong code ever anyway!
>
>
> True. There are many ways to write bugs. The question is whether the
> language makes those bugs easier to spot or does it obfuscate them? Making
> await explicit every time makes this bug easier to see. If I were doing a
> code review I could spot that bug. Without the explicit await it would be
> much harder to spot by just reading the code. It’s not clear whether it’s
> even a bug unless you already know that “processImage” is an async function.
>
> This example also shows once again the importance of returning to the
>> right queue. If the “await downloadImage” continues on some other queue
>> then you would be using UIKit on a background thread, which is not allowed.
>> It seems like we’re starting to see some convergence on this idea, which
>> I’m glad to see.
>>
>
>
> The proposal already covered this:
>
>   await DispatchQueue.main.syncCoroutine()
>
>
> As I’ve mentioned before, the proposal is a proposal. This is the
> discussion of the proposal. I think it’s reasonable for us to disagree with
> the proposal, especially if we can explain why we think it should change.
>
> As someone who has used C#’s async/await feature (for iOS apps, even) I
> know first-hand how useful this feature can be (as well as its pitfalls).
> I’m excited about the prospect of using it in Swift. That’s why I want it
> to be as good as it can be. As proposed I don’t think it is as good as it
> can be. That’s why I’m participating in this discussion.
>
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] await keyword "scope"

2017-09-12 Thread Adam Kemp via swift-evolution


> On Sep 12, 2017, at 1:00 PM, Wallacy  wrote:
> 
> So, the problem is a predefined order to evaluate the values, not a "second" 
> await.

No, the order of execution is well-defined. The problem isn’t in which order 
the arguments are executed. The problem is when that evaluation happens with 
respect to when we return to the run loop. That’s an entirely new problem with 
async/await. In Swift today it is not possible to write code that returns to 
the run loop in between evaluation of arguments.

> Like you said, the person can write the wrong code ever anyway!

True. There are many ways to write bugs. The question is whether the language 
makes those bugs easier to spot or does it obfuscate them? Making await 
explicit every time makes this bug easier to see. If I were doing a code review 
I could spot that bug. Without the explicit await it would be much harder to 
spot by just reading the code. It’s not clear whether it’s even a bug unless 
you already know that “processImage” is an async function.

> This example also shows once again the importance of returning to the right 
> queue. If the “await downloadImage” continues on some other queue then you 
> would be using UIKit on a background thread, which is not allowed. It seems 
> like we’re starting to see some convergence on this idea, which I’m glad to 
> see.
> 
> 
> The proposal already covered this:
> 
>   await DispatchQueue.main.syncCoroutine()

As I’ve mentioned before, the proposal is a proposal. This is the discussion of 
the proposal. I think it’s reasonable for us to disagree with the proposal, 
especially if we can explain why we think it should change.

As someone who has used C#’s async/await feature (for iOS apps, even) I know 
first-hand how useful this feature can be (as well as its pitfalls). I’m 
excited about the prospect of using it in Swift. That’s why I want it to be as 
good as it can be. As proposed I don’t think it is as good as it can be. That’s 
why I’m participating in this discussion.___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] await keyword "scope"

2017-09-12 Thread Wallacy via swift-evolution
Em ter, 12 de set de 2017 às 16:48, Adam Kemp via swift-evolution <
swift-evolution@swift.org> escreveu:

>
> I think that decision makes sense for try/throws, but I feel like the
> await keyword is fundamentally different from that. The pitfalls of not
> understanding how the code is transformed and how it will behave at runtime
> are much greater with await than with try.
>
> If you have a line of code with multiple expressions that can throw then
> the consequences of not knowing which particular one threw the error are
> minor. In most cases it doesn’t matter, and you would handle a given error
> the same regardless of which subexpression threw the error.
>
> With await the function is actually broken up into pieces, and unrelated
> code can run in between those pieces on the same thread and/or the same
> queue. That has a much higher potential of leading to subtle bugs if you
> can’t see where those breaks are.
>
>
> What sort of bugs?  Can you please provide a concrete example we can
> discuss?
>
>
> Here’s just a simple example of code that looks right but is buggy:
>
> @IBAction func buttonDidClick(sender:AnyObject) {
> beginAsync {
> let image = await processImage(downloadImage(), resize:
> self.resizeSwitch.isOn)
> displayImage(image)
> }
> }
>
>
> That code I believe would be equivalent to this more explicit code:
>
> @IBAction func buttonDidClick(sender:AnyObject) {
> beginAsync {
> let downloadedImage = await downloadImage()
> let resizeSwitchIsOn = self.resizeSwitch.isOn
> let image = await processImage(downloadedImage,
> resize: resizeSwitchIsOn)
> displayImage(image)
> }
> }
>
>
> The subtlety here is that control can be returned to the run loop before
> the code checks the value of resizeSwitch.isOn. That means there is a time
> when the user can change the switch before it’s read.
>
>
Obviously someone could write the second version of this code and have the
> same bug so the problem isn’t that it’s possible to write this bug. The
> problem is that it’s not clear in the first example where those breaks are
> where control may be returned to the run loop. Someone reading the first
> example can’t tell when self.resizeSwitch.isOn is going to be read (now or
> some future iteration of the run loop).
>
>
So, the problem is a predefined order to evaluate the values, not a
"second" await.

Like you said, the person can write the wrong code ever anyway!


> The correct way to write this would be to read the UI up front:
>
> @IBAction func buttonDidClick(sender:AnyObject) {
> beginAsync {
> let resizeSwitchIsOn = self.resizeSwitch.isOn
> let downloadedImage = await downloadImage()
> let image = await processImage(downloadedImage,
> resize: resizeSwitchIsOn)
> displayImage(image)
> }
> }
>
>
>
In this case is not better discuss the precedence order to evaluate the
values?


> Based on my experience dealing with async/await code and the subtlety of
> returning to the run loop in between expressions I think the added clarity
> of an explicit await for each call outweighs the inconvenience. It is a
> hard enough adjustment for people to understand that this function executes
> in pieces with other code running in between. I think it would be an even
> harder adjustment if you couldn’t use a simple rule like “every time you
> see await there is a break right there”. In the original example there are
> two breaks in the same line, and it’s opaque to the reader where those
> breaks are.
>
>
Precedence order still be a problem using another "await" keyword.


> This example also shows once again the importance of returning to the
> right queue. If the “await downloadImage” continues on some other queue
> then you would be using UIKit on a background thread, which is not allowed.
> It seems like we’re starting to see some convergence on this idea, which
> I’m glad to see.
>


The proposal already covered this:

  await DispatchQueue.main.syncCoroutine()


Maybe not the best way, but this is one possible way.



> ___
> 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


Re: [swift-evolution] await keyword "scope"

2017-09-12 Thread Adam Kemp via swift-evolution

>> I think that decision makes sense for try/throws, but I feel like the await 
>> keyword is fundamentally different from that. The pitfalls of not 
>> understanding how the code is transformed and how it will behave at runtime 
>> are much greater with await than with try.
>> 
>> If you have a line of code with multiple expressions that can throw then the 
>> consequences of not knowing which particular one threw the error are minor. 
>> In most cases it doesn’t matter, and you would handle a given error the same 
>> regardless of which subexpression threw the error.
>> 
>> With await the function is actually broken up into pieces, and unrelated 
>> code can run in between those pieces on the same thread and/or the same 
>> queue. That has a much higher potential of leading to subtle bugs if you 
>> can’t see where those breaks are.
> 
> What sort of bugs?  Can you please provide a concrete example we can discuss?

Here’s just a simple example of code that looks right but is buggy:

@IBAction func buttonDidClick(sender:AnyObject) {
beginAsync {
let image = await processImage(downloadImage(), resize: 
self.resizeSwitch.isOn)
displayImage(image)
}
}

That code I believe would be equivalent to this more explicit code:

@IBAction func buttonDidClick(sender:AnyObject) {
beginAsync {
let downloadedImage = await downloadImage()
let resizeSwitchIsOn = self.resizeSwitch.isOn
let image = await processImage(downloadedImage, resize: 
resizeSwitchIsOn)
displayImage(image)
}
}

The subtlety here is that control can be returned to the run loop before the 
code checks the value of resizeSwitch.isOn. That means there is a time when the 
user can change the switch before it’s read.

Obviously someone could write the second version of this code and have the same 
bug so the problem isn’t that it’s possible to write this bug. The problem is 
that it’s not clear in the first example where those breaks are where control 
may be returned to the run loop. Someone reading the first example can’t tell 
when self.resizeSwitch.isOn is going to be read (now or some future iteration 
of the run loop).

The correct way to write this would be to read the UI up front:

@IBAction func buttonDidClick(sender:AnyObject) {
beginAsync {
let resizeSwitchIsOn = self.resizeSwitch.isOn
let downloadedImage = await downloadImage()
let image = await processImage(downloadedImage, resize: 
resizeSwitchIsOn)
displayImage(image)
}
}

Based on my experience dealing with async/await code and the subtlety of 
returning to the run loop in between expressions I think the added clarity of 
an explicit await for each call outweighs the inconvenience. It is a hard 
enough adjustment for people to understand that this function executes in 
pieces with other code running in between. I think it would be an even harder 
adjustment if you couldn’t use a simple rule like “every time you see await 
there is a break right there”. In the original example there are two breaks in 
the same line, and it’s opaque to the reader where those breaks are.

This example also shows once again the importance of returning to the right 
queue. If the “await downloadImage” continues on some other queue then you 
would be using UIKit on a background thread, which is not allowed. It seems 
like we’re starting to see some convergence on this idea, which I’m glad to see.___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] await keyword "scope"

2017-09-11 Thread Chris Lattner via swift-evolution

> On Sep 11, 2017, at 1:42 PM, Adam Kemp  wrote:
> 
>> Yes, this is a reasonable concern.  We debated it heavily in the Swift 2 
>> timeframe when introducing error handling (as other’s have pointed out, it 
>> works the same way).
>> 
>> This is a tradeoff between the readability benefits of marking non-obvious 
>> control flow vs the readability disadvantage of having noise in the code. 
>> Requiring marking on every async or throwing call is particularly bad in the 
>> case of chaining.  Compare:
>> 
>>let x = try (try (try a.foo()).bar()).baz()
>> vs:
>>let x = try a.foo().bar().baz()
>> 
>> In the Swift 2 timeframe, we decided that in many cases, it is mostly 
>> obvious what APIs can throw, so one marker is enough.  That said, there ARE 
>> potentially confusing cases, and some programmers may want to be more 
>> explicit about marking.  This is why the compiler allows you to explicitly 
>> mark subexpressions if you’d like.
>> 
>> I believe that this design has served the community well, and I haven’t 
>> heard of serious problems with it.  I’m pretty confident that async 
>> following the same model will have similar success.
>> 
>> -Chris
>> 
> 
> I think that decision makes sense for try/throws, but I feel like the await 
> keyword is fundamentally different from that. The pitfalls of not 
> understanding how the code is transformed and how it will behave at runtime 
> are much greater with await than with try.
> 
> If you have a line of code with multiple expressions that can throw then the 
> consequences of not knowing which particular one threw the error are minor. 
> In most cases it doesn’t matter, and you would handle a given error the same 
> regardless of which subexpression threw the error.
> 
> With await the function is actually broken up into pieces, and unrelated code 
> can run in between those pieces on the same thread and/or the same queue. 
> That has a much higher potential of leading to subtle bugs if you can’t see 
> where those breaks are.

What sort of bugs?  Can you please provide a concrete example we can discuss?

-Chris

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


Re: [swift-evolution] await keyword "scope"

2017-09-11 Thread Adam Kemp via swift-evolution


> On Aug 28, 2017, at 11:19 PM, Chris Lattner  wrote:
> 
> 
>> On Aug 28, 2017, at 1:09 PM, Adam Kemp via swift-evolution 
>> > wrote:
>> 
>> I decided to split this out to its own thread because it seems orthogonal to 
>> other issues being discussed.
>> 
>> When I read this line from the proposal:
>> 
>> await decodeImage(dataResource.get(), imageResource.get())
>> 
>> It’s not clear to me where the asynchronous call is. There are three 
>> function calls on that line. Which ones might actually suspend? You can’t 
>> tell by looking at it because there’s only one await keyword (the answer is 
>> all 3).
> 
> Yes, this is a reasonable concern.  We debated it heavily in the Swift 2 
> timeframe when introducing error handling (as other’s have pointed out, it 
> works the same way).
> 
> This is a tradeoff between the readability benefits of marking non-obvious 
> control flow vs the readability disadvantage of having noise in the code. 
> Requiring marking on every async or throwing call is particularly bad in the 
> case of chaining.  Compare:
> 
>let x = try (try (try a.foo()).bar()).baz()
> vs:
>let x = try a.foo().bar().baz()
> 
> In the Swift 2 timeframe, we decided that in many cases, it is mostly obvious 
> what APIs can throw, so one marker is enough.  That said, there ARE 
> potentially confusing cases, and some programmers may want to be more 
> explicit about marking.  This is why the compiler allows you to explicitly 
> mark subexpressions if you’d like.
> 
> I believe that this design has served the community well, and I haven’t heard 
> of serious problems with it.  I’m pretty confident that async following the 
> same model will have similar success.
> 
> -Chris
> 

I think that decision makes sense for try/throws, but I feel like the await 
keyword is fundamentally different from that. The pitfalls of not understanding 
how the code is transformed and how it will behave at runtime are much greater 
with await than with try.

If you have a line of code with multiple expressions that can throw then the 
consequences of not knowing which particular one threw the error are minor. In 
most cases it doesn’t matter, and you would handle a given error the same 
regardless of which subexpression threw the error.

With await the function is actually broken up into pieces, and unrelated code 
can run in between those pieces on the same thread and/or the same queue. That 
has a much higher potential of leading to subtle bugs if you can’t see where 
those breaks are.

That’s why I think that it is important for every one of those locations to be 
explicitly marked so that it is very clear where the breaks in the function 
are, and thus where other code can run.___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] await keyword "scope"

2017-08-29 Thread Chris Lattner via swift-evolution

> On Aug 28, 2017, at 1:09 PM, Adam Kemp via swift-evolution 
>  wrote:
> 
> I decided to split this out to its own thread because it seems orthogonal to 
> other issues being discussed.
> 
> When I read this line from the proposal:
> 
> await decodeImage(dataResource.get(), imageResource.get())
> 
> It’s not clear to me where the asynchronous call is. There are three function 
> calls on that line. Which ones might actually suspend? You can’t tell by 
> looking at it because there’s only one await keyword (the answer is all 3).

Yes, this is a reasonable concern.  We debated it heavily in the Swift 2 
timeframe when introducing error handling (as other’s have pointed out, it 
works the same way).

This is a tradeoff between the readability benefits of marking non-obvious 
control flow vs the readability disadvantage of having noise in the code. 
Requiring marking on every async or throwing call is particularly bad in the 
case of chaining.  Compare:

   let x = try (try (try a.foo()).bar()).baz()
vs:
   let x = try a.foo().bar().baz()

In the Swift 2 timeframe, we decided that in many cases, it is mostly obvious 
what APIs can throw, so one marker is enough.  That said, there ARE potentially 
confusing cases, and some programmers may want to be more explicit about 
marking.  This is why the compiler allows you to explicitly mark subexpressions 
if you’d like.

I believe that this design has served the community well, and I haven’t heard 
of serious problems with it.  I’m pretty confident that async following the 
same model will have similar success.

-Chris

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


Re: [swift-evolution] await keyword "scope"

2017-08-28 Thread Félix Cloutier via swift-evolution
I don't believe that having "await" everywhere will remind you that you have to 
precalculate and cache operands to suspending operations. In fact, it sounds 
like your goal is not to enforce that every async operation is gated behind 
`await`, but to ensure that developers cache the operands of asynchronous 
operations if they need to do it. IMO, you're choosing the wrong tool for this. 
`Await` goes on the operation, not on the operands; but it's the operands that 
you're trying to protect. This example has one `await` per asynchronous 
operation but it still exhibits the problem that you're hoping to solve:

> let webProfile = await loadWebResource(self.webProfilePath)
> let image = await loadWebResource(self.imagePath)
> await decodeImage(webProfile, image)

This code doesn't feel any more wrong than your single statement with three 
asynchronous operations, and to me that means that your proposed solution is 
insufficient to address the problem that you describe (if there's a problem at 
all).

Additionally, in the majority of cases, `await`ing at every async expression 
will be just noise. For instance, `await foo(await bar())` can't possibly 
exhibit the problem that you describe because bar() is guaranteed to complete 
before foo() can start. In that case, it's just noise.

Félix

> Le 28 août 2017 à 13:37, Adam Kemp via swift-evolution 
>  a écrit :
> 
> I explained what the value is already. It identifies clearly in your code 
> where the suspension points are. Each place you see “await” would mark a 
> location where your code may yield and allow other things to happen. Those 
> are points where state could change unexpectedly. It’s important for someone 
> writing asynchronous code to understand where those locations are. It’s 
> difficult to reason about your code without that knowledge.
> 
>> On Aug 28, 2017, at 1:16 PM, Wallacy > > wrote:
>> 
>> 
>> "Try" does the same, but I do not know anyone who prefers to repeat the same 
>> keyword several times.
>> 
>> return try func0(try func1(), try func2())
>> 
>> I do not think there's any value in knowing how many interim steps also need 
>> "await" ... In practice, you have to wait for everyone anyway.
>> 
>> 
>> 
>> Em seg, 28 de ago de 2017 às 17:09, Adam Kemp via swift-evolution 
>> > escreveu:
>> I decided to split this out to its own thread because it seems orthogonal to 
>> other issues being discussed.
>> 
>> When I read this line from the proposal:
>> 
>> await decodeImage(dataResource.get(), imageResource.get())
>> 
>> It’s not clear to me where the asynchronous call is. There are three 
>> function calls on that line. Which ones might actually suspend? You can’t 
>> tell by looking at it because there’s only one await keyword (the answer is 
>> all 3).
>> 
>> I’m not a fan of the ambiguity of applying the await keyword to an entire 
>> statement. I know some people think this is a good thing, but to me it’s 
>> just obfuscating important information.
>> 
>> Further, if you’re going beyond a single expression then why you would stop 
>> at the statement level? Why not make it apply to the whole block or even a 
>> whole function? Why require the keyword at all? It doesn’t appear to be 
>> adding any value if it doesn’t specify exactly where the suspension point 
>> is. “Somewhere on this line” can get rather vague.
>> 
>> async/await can be a huge win for clarity, but it also comes with the 
>> downside of having to think a bit more about what can happen at these 
>> suspension points. I feel like it should be a goal to make it very clear 
>> where those suspension points are so that we can more easily reason about 
>> them. That’s why I prefer restricting it to apply to a single expression. 
>> It’s very clear where the function gets suspended, which means it’s clearer 
>> where you need to be concerned about things possibly happening in between 
>> your code. Consider this, for example:
>> 
>> await decodeImage(loadWebResource(self.webProfilePath), 
>> loadWebResource(self.imagePath))
>> 
>> If webProfilePath and imagePath are vars then they could change in between 
>> those two calls. If you can’t see that these calls are suspending then you 
>> might not know to cache them up-front to ensure consistency:
>> 
>> let webProfilePath = self.webProfilePath
>> let imagePath = self.imagePath
>> await decodeImage(await loadWebResource(webProfilePath), await 
>> loadWebResource(imagePath))
>> 
>> I think a general guideline we should use when considering how this feature 
>> should work is to ask whether it makes bugs more likely or less likely. The 
>> goal should be to reduce bugs while simplifying code. If we simplify the 
>> code to the point where we’re making some bugs too subtle then we may be 
>> doing more harm than good.
>> ___
>> swift-evolution 

Re: [swift-evolution] await keyword "scope"

2017-08-28 Thread David Hart via swift-evolution

> On 28 Aug 2017, at 22:37, Adam Kemp via swift-evolution 
>  wrote:
> 
> I explained what the value is already. It identifies clearly in your code 
> where the suspension points are. Each place you see “await” would mark a 
> location where your code may yield and allow other things to happen. Those 
> are points where state could change unexpectedly. It’s important for someone 
> writing asynchronous code to understand where those locations are. It’s 
> difficult to reason about your code without that knowledge.

I understand the value you perceive but I think that the try and await solution 
of applying to the whole statement is a good compromise to greatly improve 
readability at the expense of a little lost information.

>> On Aug 28, 2017, at 1:16 PM, Wallacy  wrote:
>> 
>> 
>> "Try" does the same, but I do not know anyone who prefers to repeat the same 
>> keyword several times.
>> 
>> return try func0(try func1(), try func2())
>> 
>> I do not think there's any value in knowing how many interim steps also need 
>> "await" ... In practice, you have to wait for everyone anyway.
>> 
>> 
>> 
>>> Em seg, 28 de ago de 2017 às 17:09, Adam Kemp via swift-evolution 
>>>  escreveu:
>>> I decided to split this out to its own thread because it seems orthogonal 
>>> to other issues being discussed.
>>> 
>>> When I read this line from the proposal:
>>> 
>>> await decodeImage(dataResource.get(), imageResource.get())
>>> 
>>> It’s not clear to me where the asynchronous call is. There are three 
>>> function calls on that line. Which ones might actually suspend? You can’t 
>>> tell by looking at it because there’s only one await keyword (the answer is 
>>> all 3).
>>> 
>>> I’m not a fan of the ambiguity of applying the await keyword to an entire 
>>> statement. I know some people think this is a good thing, but to me it’s 
>>> just obfuscating important information.
>>> 
>>> Further, if you’re going beyond a single expression then why you would stop 
>>> at the statement level? Why not make it apply to the whole block or even a 
>>> whole function? Why require the keyword at all? It doesn’t appear to be 
>>> adding any value if it doesn’t specify exactly where the suspension point 
>>> is. “Somewhere on this line” can get rather vague.
>>> 
>>> async/await can be a huge win for clarity, but it also comes with the 
>>> downside of having to think a bit more about what can happen at these 
>>> suspension points. I feel like it should be a goal to make it very clear 
>>> where those suspension points are so that we can more easily reason about 
>>> them. That’s why I prefer restricting it to apply to a single expression. 
>>> It’s very clear where the function gets suspended, which means it’s clearer 
>>> where you need to be concerned about things possibly happening in between 
>>> your code. Consider this, for example:
>>> 
>>> await decodeImage(loadWebResource(self.webProfilePath), 
>>> loadWebResource(self.imagePath))
>>> 
>>> If webProfilePath and imagePath are vars then they could change in between 
>>> those two calls. If you can’t see that these calls are suspending then you 
>>> might not know to cache them up-front to ensure consistency:
>>> 
>>> let webProfilePath = self.webProfilePath
>>> let imagePath = self.imagePath
>>> await decodeImage(await loadWebResource(webProfilePath), await 
>>> loadWebResource(imagePath))
>>> 
>>> I think a general guideline we should use when considering how this feature 
>>> should work is to ask whether it makes bugs more likely or less likely. The 
>>> goal should be to reduce bugs while simplifying code. If we simplify the 
>>> code to the point where we’re making some bugs too subtle then we may be 
>>> doing more harm than good.
>>> ___
>>> 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
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] await keyword "scope"

2017-08-28 Thread Adam Kemp via swift-evolution
I explained what the value is already. It identifies clearly in your code where 
the suspension points are. Each place you see “await” would mark a location 
where your code may yield and allow other things to happen. Those are points 
where state could change unexpectedly. It’s important for someone writing 
asynchronous code to understand where those locations are. It’s difficult to 
reason about your code without that knowledge.

> On Aug 28, 2017, at 1:16 PM, Wallacy  wrote:
> 
> 
> "Try" does the same, but I do not know anyone who prefers to repeat the same 
> keyword several times.
> 
> return try func0(try func1(), try func2())
> 
> I do not think there's any value in knowing how many interim steps also need 
> "await" ... In practice, you have to wait for everyone anyway.
> 
> 
> 
> Em seg, 28 de ago de 2017 às 17:09, Adam Kemp via swift-evolution 
> > escreveu:
> I decided to split this out to its own thread because it seems orthogonal to 
> other issues being discussed.
> 
> When I read this line from the proposal:
> 
> await decodeImage(dataResource.get(), imageResource.get())
> 
> It’s not clear to me where the asynchronous call is. There are three function 
> calls on that line. Which ones might actually suspend? You can’t tell by 
> looking at it because there’s only one await keyword (the answer is all 3).
> 
> I’m not a fan of the ambiguity of applying the await keyword to an entire 
> statement. I know some people think this is a good thing, but to me it’s just 
> obfuscating important information.
> 
> Further, if you’re going beyond a single expression then why you would stop 
> at the statement level? Why not make it apply to the whole block or even a 
> whole function? Why require the keyword at all? It doesn’t appear to be 
> adding any value if it doesn’t specify exactly where the suspension point is. 
> “Somewhere on this line” can get rather vague.
> 
> async/await can be a huge win for clarity, but it also comes with the 
> downside of having to think a bit more about what can happen at these 
> suspension points. I feel like it should be a goal to make it very clear 
> where those suspension points are so that we can more easily reason about 
> them. That’s why I prefer restricting it to apply to a single expression. 
> It’s very clear where the function gets suspended, which means it’s clearer 
> where you need to be concerned about things possibly happening in between 
> your code. Consider this, for example:
> 
> await decodeImage(loadWebResource(self.webProfilePath), 
> loadWebResource(self.imagePath))
> 
> If webProfilePath and imagePath are vars then they could change in between 
> those two calls. If you can’t see that these calls are suspending then you 
> might not know to cache them up-front to ensure consistency:
> 
> let webProfilePath = self.webProfilePath
> let imagePath = self.imagePath
> await decodeImage(await loadWebResource(webProfilePath), await 
> loadWebResource(imagePath))
> 
> I think a general guideline we should use when considering how this feature 
> should work is to ask whether it makes bugs more likely or less likely. The 
> goal should be to reduce bugs while simplifying code. If we simplify the code 
> to the point where we’re making some bugs too subtle then we may be doing 
> more harm than good.
> ___
> 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


Re: [swift-evolution] await keyword "scope"

2017-08-28 Thread Wallacy via swift-evolution
"Try" does the same, but I do not know anyone who prefers to repeat the
same keyword several times.

return try func0(try func1(), try func2())

I do not think there's any value in knowing how many interim steps also
need "await" ... In practice, you have to wait for everyone anyway.



Em seg, 28 de ago de 2017 às 17:09, Adam Kemp via swift-evolution <
swift-evolution@swift.org> escreveu:

> I decided to split this out to its own thread because it seems orthogonal
> to other issues being discussed.
>
> When I read this line from the proposal:
>
> await decodeImage(dataResource.get(), imageResource.get())
>
>
> It’s not clear to me where the asynchronous call is. There are three
> function calls on that line. Which ones might actually suspend? You can’t
> tell by looking at it because there’s only one await keyword (the answer is
> all 3).
>
> I’m not a fan of the ambiguity of applying the await keyword to an entire
> statement. I know some people think this is a good thing, but to me it’s
> just obfuscating important information.
>
> Further, if you’re going beyond a single expression then why you would
> stop at the statement level? Why not make it apply to the whole block or
> even a whole function? Why require the keyword at all? It doesn’t appear to
> be adding any value if it doesn’t specify exactly where the suspension
> point is. “Somewhere on this line” can get rather vague.
>
> async/await can be a huge win for clarity, but it also comes with the
> downside of having to think a bit more about what can happen at these
> suspension points. I feel like it should be a goal to make it very clear
> where those suspension points are so that we can more easily reason about
> them. That’s why I prefer restricting it to apply to a single expression.
> It’s very clear where the function gets suspended, which means it’s clearer
> where you need to be concerned about things possibly happening in between
> your code. Consider this, for example:
>
>
> await decodeImage(loadWebResource(self.webProfilePath), 
> loadWebResource(self.imagePath))
>
>
> If webProfilePath and imagePath are vars then they could change in between
> those two calls. If you can’t see that these calls are suspending then you
> might not know to cache them up-front to ensure consistency:
>
> let webProfilePath = self.webProfilePath
> let imagePath = self.imagePath
>
> await decodeImage(await loadWebResource(webProfilePath), await
> loadWebResource(imagePath))
>
>
> I think a general guideline we should use when considering how this
> feature should work is to ask whether it makes bugs more likely or less
> likely. The goal should be to reduce bugs while simplifying code. If we
> simplify the code to the point where we’re making some bugs too subtle then
> we may be doing more harm than good.
> ___
> 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