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

Rod Brown rodney.brown6 at icloud.com
Tue Jul 5 18:11:13 CDT 2016


I definitely see Dave’s point here.

It seems to me we can only get generic to the point at which the semantics change. So methods that don’t have any mutating consequences can be applied to any value such as Structs and Classes, but when the semantic relates to mutation, we can only apply that to “All structs” or “all classes” as the behaviour changes dramatically how we deal with it. I can’t see any way around that, but I’d love to see any ideas how we could avoid it.

I think this is definitely somewhere we need a way to declare a protocol can only be implemented on a value type, much like we can say that protocol is only available on classes today.

- Rod


> On 6 Jul 2016, at 7:11 AM, Dave Abrahams via swift-evolution <swift-evolution at swift.org> wrote:
> 
> 
> on Tue Jul 05 2016, Jonathan Hull <jhull-AT-gbis.com <http://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.
> 
> -- 
> Dave
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160706/bd2a1f9c/attachment-0001.html>


More information about the swift-evolution mailing list