> 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