[swift-evolution] Class mutation model and value-constrained protocols

Dave Abrahams dabrahams at apple.com
Tue Jul 5 12:53:35 CDT 2016


A “mutating” keyword on a protocol method is only useful if
you can reason about the mutation in generic code.  

  protocol P {
    mutating func changeMe()
    func noChange() -> Int
  }

In other words, given x of some type conforming to P, it should be
meaningful that this is an error:

  func f<T: P>(x: T) {
    immutable.changeMe() // can't mutate a "let" binding
  }

which means that you shouldn't be able to get around that meaning by
writing:

  func g<T: P>(x: T) {
    var mutable = x
    mutable.changeMe() // OK
  }

Also, you should be able to reason that both of the following print the same
thing twice, for types whose methods have no external side-effects:

  func gg<T: P>(x: T) {
    print(x)
    x.noChange()
    print(x)
  }

  func ggg<T: P>(x: T) {
    print(x)
    var y = x
    y.changeMe()
    print(x)
  }

When T is a class type, it can easily violate *all* of these
expectations.  In other words, classes naturally bypass the mutation
model.

If we are going to maintain source stability after Swift 3, it seems
that we either need to address this now, or decide that it won't be
addressed, because of the “viral const” problem.

One idea that Jordan and I have floated is that protocols with mutating
methods should be constrained to applying to non-class types.  That
would be a step in the right direction, but, that still leaves cases
like gg able to easily violate expectations when the protocol in
question has no mutating methods.

Another possibility would be to formalize the idea of value semantics in
protocol declarations, so that non-class protocols were only allowed to
apply to values.

It's also possible that I've overestimated the seriousness of the issue
and we actually can afford to postpone thinking about it until after
Swift 4.

Thoughts?

-- 
Dave



More information about the swift-evolution mailing list