Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to the review manager via the forum messaging feature. When contacting the review manager directly, please keep the proposal link at the top of the message.
Trying it out
If you'd like to try this proposal out, you can download a toolchain supporting it. You will need to use a recent main development snapshot and to enable the experimental feature flags outlined in the proposal.
What goes into a review?
The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:
What is your evaluation of the proposal?
Is the problem being addressed significant enough to warrant a change to Swift?
Does this proposal fit well with the feel and direction of Swift?
If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
The proposal doesn't seem to say what kinds of accessor can witness a yielding mutate protocol requirement (nor does it disallow creating a yielding mutate protocol requirement).
I'm still against this proliferation of accessors; I think it further damages the language with significant and beginner-unfriendly complexity, and in particular, splits the ecosystem around the kinds of accessors required by public protocols.
As an alternative to proliferating accessors, I think we should instead commit to first-class non-escaping reference types like Rust. I'm disappointed not to see that under "alternatives considered".
-1 from me. Back to the drawing board on the whole vision.
This is demonstrably false: The proposed accessors are functionally identical to simple getters and setters for copyable types (ignoring the performance implications, of course). For non-copyable types, the complexity stems from the non-copyable type itself and the new accessors merely fill the gap that any user of non-copyable types will stumble upon anyway.
This is also demonstrably a very bad idea. References conceptually come in two major forms:
An address (this is how C++ references are implemented): These are extremely limited in their utility and they will be overwhelmingly overlooked in favor of explicit accessor functions for the purpose of flexibility and API stability (exactly how a lot of C++ code handles this).
An abstraction over accessor sets (this is how inout bindings work in Swift): These merely offer a convenient way to forward a set of accessors somewhere without having to reiterate them. The accessors themselves still need to be implemented.
This kind of disrespectful and condescending behavior is unacceptable in a constructive discussion environment such as the Swift forums.
I went through these in far more detail on the other threads about accessors, and I didn't want to relitigate it all here, but…
This is my concern; any public protocol which wishes to support noncopyables for an associated type will be forced into the { yielding borrow yielding mutate } lane, which makes the learning curve for the protocol instantly include noncopyables, even if the eventual witness for a given implementor uses a copyable type and can actually use get/set (related: my first question about whether get+set can witness yielding mutate).
Yes, this one. Rust's &T and &mut T. It seems pretty clear to me (as we create Span, MutableSpan, etc.), that we're going to end up here anyway. If we make struct Reference<T>: ~Escapable and struct MutableReference<T>: ~Escapable and some syntactic sugar to allow borrowing from and assigning through such references, we cover most of what the accessor vision document seeks to offer. We can potentially allow var t: T { get set } to be witnessed by @lifetime(borrow self) var t: MutableReference<T> { borrowing get } or similar, and vice versa, where T is Copyable. It's not a perfect solution by any means, but it keeps the proliferation of new concepts relatively low, compared to the accessor vision, by leaning on all the other new things that have been created recently. We also have the experience of Rust to say, yes, this is enough to build a language and stdlib on top of it.
The major place that yielding accessors provide functionality that the references don't, is that they can transform the data or run cleanup code after the borrow ends; that is, this:
is not true, there is actually new functionality here. I personally don't value this new functionality highly (compared to keeping the language simple, slightly complicating the interface of Dictionary for noncopyable types doesn't concern me greatly).
Except that this isn't new. _modify and _read accessors already support this and are used frequently in both the standard library and in the wild. This proposal is merely stabilizing those existing features.
As for accessors returning inescapable references, I'd rather just have generalized borrowing & inout bindings.
Might have found a typo in section "Non-yieldingborrow and mutate accessors"
struct Pair<Left : ~Copyable, Right : ~Copyable> : ~Copyable {
var left: Left
var right: Right
var reversed: Pair<Right, Left> {
yielding borrow { // <- POSSIBLE TYPO?
let result = Pair<Right, Left>(left: right, right: left)
yield result
self = .init(left: result.right, right: result.left)
}
}
}
It's a little bit surprising for me we can re-assign to self in a yielding borrow. In the original pitch [Pitch] Modify and read accessors, it was spelled as mutating read, so maybe this should be mutating yielding borrow?
PS: I think the gap is, maybe we should clarify the semantics of self in a yielding borrow of non-copyable types in the first place: is it a borrowing self or a consuming self? And, is consuming yielding borrow a valid accessor?