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

Kevin Ballard kevin at sb.org
Thu Dec 10 18:03:17 CST 2015


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
> https://lists.swift.org/mailman/listinfo/swift-evolution
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151210/231ce9b7/attachment.html>


More information about the swift-evolution mailing list