There's not question in my mind that it should behave the same as throwing function calls with inout parameters: setters are run when an exception is thrown in such a case; so setters must be run when yield fails too. I think it'd be problematic if yield behaved any differently.
To be clear, all the code you see in the generator would run as written until the end of the function. If you have yield &a it'll run the getter and setter of &a (or _modify) even if there's no one to yield to anymore, just as if you were passing it inout to a function with an empty body. There is really no error path in the generator (unless you write one explicitly), it just runs it course.
While maybe not optimal, the generator should behave correctly even if you don't give a single though about errors, and that's the point.
The last example of my last post illustrates how good a non-failing yield is at expressing the control flow. Here's the equivalent rewrite using defer assuming a yield that exits the generator on failure:
func generate() {
do {
var (a, b) = fetchAB()
defer { commitAB(a, b) }
yield &a
yield &b
}
do {
var (c, d) = fetchCD()
defer { commitCD(c, d) }
yield &c
yield &d
}
}
The problem is it's likely you'll forget to put the commit calls in a defer with a do block for correctly scoping those defer. I said "likely you'll forget", but I should have said you'll definitely forget it you aren't mindful of the error path while writing the code.
Using finally makes the flow a bit more linear, but doesn't really help to remind you you need to write all this control flow in the first place:
func generate() {
do {
var (a, b) = fetchAB()
yield &a
yield &b
} finally {
commitAB(a, b)
}
do {
var (c, d) = fetchCD()
yield &c
yield &d
} finally {
commitCD(c, d)
}
}
I didn't want to write an example using yield β¦ else because it's just awful having to duplicate those commit calls. That said, the obligatory else would be a good reminder of the error path. People generally want to focus on what they want the code to do when writing code, not error handling and they might see the compiler complaining as an annoyance you need to silence by writing an empty else. If you do that you end up with the same problem as above, but at least you were warned I guess.
I think it's far better if we can just get rid of the error paths when they're not needed.