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

Jonathan Hull jhull at gbis.com
Tue Jul 5 14:03:04 CDT 2016


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)


> 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.

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...

> 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.

Thanks,
Jon



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160705/52887071/attachment.html>


More information about the swift-evolution mailing list