> On Jan 3, 2018, at 5:33 PM, Hooman Mehr <hoo...@mac.com> wrote:
> 
> Thank you Slava, it is a very insightful answer. 
> 
> It also reveals a potential source for hard to track bugs. To make it easier 
> to see, lets add state to class C:
> 
> class D: C { var state = 0 }
> 
> var d = D() // Bad but common habit of declaring objects as var
> for i in 1...5 { d.f(i); d.state += 1 }
> print(d.state) // Prints 1
> 
> The result is surprising because a typical programmer does not expect an 
> object to have mutating methods (that replace the object itself with a 
> different one).
> 
> I think this is a bug in Swift compiler. It should not let a class “inherit” 
> a mutating method in this manner. Compiler should require a non-mutating 
> declaration of `f()` to satisfy `P` conformance for a class type.

Perhaps, but it’s also possible to define a mutating method g() in a protocol 
extension of P, where g() is not a protocol requirement. Calling g() on a class 
instance can “replace” self in this manner also.

> If we fix the above, it should be possible to do what I asked in my post: 
> When compiler knows an existential is an object, it should know all methods 
> are non-mutating and accept `let` declaration despite calls to nominally 
> mutating methods. Also, if a protocol refines another protocol to be 
> class-bound, compiler should automatically refine all of its inherited 
> mutating methods to be non-mutating and allow `let` declaration of an 
> existential of that protocol even if there are calls to those originally 
> mutating methods. 

This will prevent you from defining default implementations of protocol 
requirements in protocol extensions, which would be a source-breaking change at 
this point. I don’t think we can make such a change now.

Slava

> 
> Hooman
> 
>> On Dec 21, 2017, at 10:59 PM, Slava Pestov <spes...@apple.com 
>> <mailto:spes...@apple.com>> wrote:
>> 
>> Hi Hooman,
>> 
>> Since the protocol P is not class-bounded, the requirement can be witnessed 
>> by a protocol extension method which re-assigns ‘self’:
>> 
>> protocol Initable {
>>   init()
>> }
>> 
>> extension P where Self : Initable {
>>   mutating func f(_ x: Int) -> Int {
>>     self = Self()
>>     return x
>>   }
>> }
>> 
>> class C : P, Initable {
>>   required init() {}
>> }
>> 
>> Now imagine you could do this,
>> 
>> let x: P & AnyObject
>> 
>> x.f(12)
>> 
>> This would be invalid because ‘x’ is a let binding but the requirement ‘f’ 
>> is witnessed by the protocol extension method, which performs a mutating 
>> access of ‘self’.
>> 
>> Slava
>> 
>>> On Dec 21, 2017, at 6:01 PM, Hooman Mehr via swift-evolution 
>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>> 
>>> The title is confusing, let me clarify by example:
>>> 
>>> We have this protocol with a mutating method:
>>> 
>>> protocol P { mutating func f(_ x: Int) -> Int }
>>> 
>>> And a conforming class (which has to conform with a non-mutating method):
>>> 
>>> class C: P { func f(_ x: Int) -> Int { return x } }
>>> 
>>> An instance of this class can be used with a let constant:
>>> 
>>> let c = C()
>>> c.f(1) // OK
>>>  
>>> If we make it an existential object conforming to P, the immutability of 
>>> the method will be erased:
>>> 
>>> let c: AnyObject & P = C()
>>> c.f(1) // Cannot use mutating member on immutable value: 'c' is a 'let' 
>>> constant
>>> 
>>> A generic context has the same issue:
>>> 
>>> func f<T: AnyObject & P>(_ arg: T)-> Int { return arg.f(1) } // Cannot use 
>>> mutating member on immutable value: ‘arg' is a 'let' constant
>>> 
>>> My question: 
>>> 
>>> Is it too much work to preserve method non-mutability in in these cases?
>>> 
>>> The workaround I am using is this:
>>> 
>>> protocol Q: class, P { func f(_ x: Int) -> Int } // 'Refine' it to be 
>>> non-mutating.
>>> extension C: Q {}
>>> 
>>> // Now these work:
>>> let c: Q = C()
>>> c.f(1) // OK
>>> func f<T: Q>(_ arg: T)-> Int { return arg.f(1) } // OK
>>> 
>>> This workaround creates a lot of duplication and is hard to maintain. It is 
>>> not something that I do often, but when I do, it is pretty annoying. 
>>> 
>>> Supplemental questions:
>>> 
>>> Have you guys ever ran into this?
>>> Is there already a bug tracking this? 
>>> 
>>> 
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> 
> 

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to