[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