[swift-evolution] Proposal: Intermediate mutation qualifier for protocol functions on reference-types

Josh Avant mailinglists at iamjo.sh
Fri Dec 11 11:22:22 CST 2015


Thanks, Kevin!

Look forward to any other feedback!

On Thursday, December 10, 2015, Kevin Ballard via swift-evolution <
swift-evolution at swift.org> wrote:

> A couple of miscellaneous points:
>
> * This problem only occurs when the class inherits a default
> implementation of the method from a protocol extension. If the class
> declares the method itself, then it's no longer a mutating method and
> everything works fine.
> * The problem exists because mutating functions are allowed to assign to
> self, but methods on class types cannot assign to self, they can only
> mutate the properties of self. This is why we cannot simply allow the call
> to the inherited mutating method, as that method may reassign self.
> * Classes can still call the method, they just have to say something like
>
> var this = self
> this.callMutatingMethod()
>
> Yeah it's a little awkward, but it's not really all that bad for an edge
> case like this.
>
> Another potential workaround requires more work on the protocol side but
> allows implementations to not care about the difference, which is to
> provide a non-mutating variant in an extension restricted by Self:
> AnyObject:
>
> protocol P {
>     var count: Int { get set }
>     mutating func foo() -> String
> }
>
> extension P {
>     mutating func foo() -> String {
>         return _mutatingFoo(&self)
>     }
> }
>
> extension P where Self: AnyObject {
>     func foo() -> String {
>         var this = self
>         return _mutatingFoo(&this)
>     }
> }
>
> private func _mutatingFoo<T: P>(inout value: T) -> String {
>     value.count += 1
>     return "foo"
> }
>
> Ultimately, I think there's some value in this proposal, but I worry about
> adding a new keyword to handle an edge case like this when there's
> workarounds available. Assuming we do add this feature, I'd suggest using
> something like `mutating(ish)` instead of inventing a brand new keyword
> (there's precedent in `private(set)`).
>
> -Kevin Ballard
>
> On Thu, Dec 10, 2015, at 02:35 PM, Josh Avant via swift-evolution wrote:
>
> Currently, when a reference-type adopts a protocol with a function
> declared as `mutating`, the reference-type's implementation cannot call
> that function internally. This is because the compiler enforces an
> immutable `self` pointer value, and the `mutating` qualifier implies that
> the function implementation may mutate that `self` pointer value.
>
> However, there seems to be a number of fairly reasonable situations where
> a reference-type implementation of these `mutating` functions may only want
> to mutate properties owned by `self`, but not the actual `self` pointer
> value.
>
> Consider this toy example:
>
> ```
> import Foundation
>
> protocol RandomDataTransformable {
>     typealias TransformableType
>     var data: [TransformableType] { get set }
>
>     mutating func addRandomData()
> }
>
> extension RandomDataTransformable where TransformableType == Int {
>     mutating func addRandomData() {
>         let random = Int(arc4random_uniform(6) + 1)
>         data.append(random)
>     }
> }
>
>
> /////
>
> // VALID
> struct NumberSource_Struct : RandomDataTransformable {
>     typealias TransformableType = Int
>     var data: [Int] = []
>
>     mutating func addData() {
>         addRandomData()
>     }
> }
>
>
> // VALID
> class NumberSource_ClassDeclaration: NSObject, RandomDataTransformable {
>     typealias TransformableType = Int
>     var data: [Int] = []
> }
>
> var numberSource = NumberSource_ClassDeclaration()
> numberSource.addRandomData()
>
>
> // INVALID
> class NumberSource_ClassImplementation: NSObject, RandomDataTransformable {
>     typealias TransformableType = Int
>     var data: [Int] = []
>
>     func addData() {
>         self.addRandomData() // Compiler Error: Cannot use mutating member
> on immutable value: 'self' is immutable
>     }
> }
> ```
>
> Even despite the fact that the default implementation for `addRandomData`
> does not mutate the `self` pointer value, reference-type implementations
> are unable to call that function internally, since it is marked as
> `mutating`.
>
> Perhaps confusingly, `addRandomData` may be called by externally, by
> objects which own instances of the reference-type (even though, again, it
> may not called internally by the implementation, itself).
>
> Currently, the only solution to allow reference-type implementations to
> call the sample `addRandomData` implementation internally is to qualify the
> whole `RandomDataTransformable` protocol as `class`. The downside here is
> that this takes an otherwise perfectly reference- and struct-compatible
> protocol + extension implementation and restricts it to only apply to
> classes, decreasing overall code reusability.
>
> My proposal would be to introduce an intermediate mutation qualifier that
> applies when protocols are adopted by reference-types. The qualifier would
> specify that the `self` pointer value itself may not be mutated, but
> `self`'s properties may be, as appropriate.
>
> Thoughts, feedback on this?
>
> *_______________________________________________*
> swift-evolution mailing list
> swift-evolution at swift.org
> <javascript:_e(%7B%7D,'cvml','swift-evolution at swift.org');>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151211/bb47c0d2/attachment.html>


More information about the swift-evolution mailing list