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

Dave Abrahams dabrahams at apple.com
Tue Jul 5 16:11:38 CDT 2016

on Tue Jul 05 2016, Jonathan Hull <jhull-AT-gbis.com> wrote:

> Comments inline
>> 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
>>   }
> Agreed.
>> 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
>>   }
> Now I am confused.  Why should this be illegal?  Doesn’t this work with structs right now? (you are working on a mutable copy)

What I meant by “you shouldn't be able get around that” was that the
code shouldn't change the value of x.

>> 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)
>>   }
> I see the issue here.  Though, it seems to me it is just a more easily
> discoverable version of an entire class of problem with reference
> types in general. X could be changed on a different thread too, for
> example.  or in a nested function call.
> You would also still have the same problem with 'let y = 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.
> I really *dislike* the approach of disallowing class types for
> protocols with mutating methods, unless an additional reference type
> is added.  I have several protocols which have conforming classes and
> structs and that that lets you choose reference vs value semantics.

How do you write generic code that works on both reference and value
types conforming to such a protocol?

> I would much rather have us mark class methods as mutating when they
> change the class’s value, and just having the concept be separate from
> let/var in that case.
>> 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.
> I would like to have a way to require value semantics in a protocol
> (similar to how we can require ‘class' now).  I still really want/need
> the ability to have a protocol which can be adhered to by both value
> and class types though...

I really want to see examples of generic code that isn't terribly tricky
to write correctly so it will work on both kinds of type.

>> 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?
> I would vote to postpone to swift 4, and have it be part of a larger
> discussion involving the marking of side-effects and thread safe
> mutability.
> If you need a stop-gap for Swift 3, I would be in favor of adding the
> ability to mark a particular protocol as needing to be a value type
> (not every protocol… just those that are marked as such).  That should
> give you the guarantees you need for particular projects.

That's a reasonable stopping point.


More information about the swift-evolution mailing list