<html><head><meta http-equiv="Content-Type" content="text/html charset=cp932"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">Dave and I have pondered this before, and considered that one possible (drastic) solution is to ban classes from implementing protocols with mutating members, on the grounds that itfs very hard to write an algorithm thatfs correct for both.</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">func removing(_ element: Element) -> Self {</div><div class=""> var result = self<i class=""> // not necessarily a copyc</i></div><div class=""> result.remove(element)</div><div class=""> return result<i class=""> // not necessarily an independent value</i></div><div class="">}</div></blockquote><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">func zapBadElements<C: RangeReplaceableCollection where C.Generator.Element == Int>(_ nums: inout C) {</div><div class=""> <i class="">// requires inout on enumsf even when itfs a class</i></div><div class=""> for i in nums.indices {</div><div class=""> if nums[i] < 0 {</div><div class=""> nums.removeAtIndex(i)</div><div class=""> }</div><div class=""> }</div><div class=""> <i class="">// cbecause of this.</i></div><div class=""><i class=""> </i>if nums.lazy.filter { $0 == 0 }.count > 5 {</div><div class=""> nums = C()</div><div class=""> }</div><div class="">}</div><div class=""><br class=""></div><div class="">var refCollection: SharedArrayOfSomeKind<Int> = c</div><div class=""><i class="">// either the variable erefCollectionf or the instance of eSharedArrayOfSomeKindf might be mutatedcor both!</i></div><div class="">zapBadElements(&refCollection)</div></blockquote><div class=""><br class=""></div>There are of course ways to safely use a protocol with mutating requirements with classes, namely if you <i class="">only</i> use them for mutation (i.e. theyfre only called from emutatingf members or on einoutf parameters) and never rely on value copying (no assignment, no returning). Most simple wrappers around mutating members would fall into this category.<div class=""><br class=""></div><div class="">We didnft really develop the idea very far yet because therefs been more pressing things to worry about. Ifm bringing it up here because itfs an important idea that shouldnft get lost.<br class=""><div class=""><div class=""><br class=""></div><div class="">---<br class=""><div class=""><br class=""></div><div class="">In lieu of this, I and a few others brought up the gincorrecth behavior of reassigning eselff in a protocol extension when the model type is a class, and got shot down. I donft have those discussions on hand at the moment, but I remember we deliberately decided to leave protocol extensions the way they were, allowing them to reassign class references. I think itfs because it means things like zapBadElements are more likely to work correctly^W as expected\if you donft have any other references at the time you do the mutation, it can work. But yeah, Ifm uncomfortable with the situation wefre in right now.</div><div class=""><br class=""></div><div class="">Jordan</div><div class=""><br class=""></div><br class=""><div><blockquote type="cite" class=""><div class="">On May 3, 2016, at 13:09, James Froggatt via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="content-type" content="text/html; charset=utf-8" class=""><div dir="auto" class=""><div class="">Thanks for the response, I agree this is currently the best solution. Unfortunately, it's not just as simple as just implementing each method, since without being able to call super, I have to fully reimplement the original behaviour, which at best seems like bad practice, and would break in future versions of Swift, and at worst could lead to hard-to-detect bugs right now.</div><div class=""><br class=""></div><div class="">To recap for anyone reading, protocol extensions currently apply mutating methods unmodified to reference types, as I found trying to make a reference-type collection. This results in the compiler disallowing eletf when calling these functions, and allows methods to reassign the reference eselff to a new object. The best solution is to manually implement each method, removing the mutating modifier<span style="background-color: rgba(255, 255, 255, 0);" class="">, yet this workaround doesn't extend to generic code.</span></div><div class=""><br class=""></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class="">To fix this behaviour, we would need to distinguish between etruef mutating functions, which reassign self, and epartiallyf mutating functions, for use in generics and protocol extensions, which can reassign properties only.</span></div><div class=""><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class="">Is there any support for making this change? Or are there any simpler solutions?</span></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class=""><br class=""></span></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class="">I did submit a bug report, but I'm pretty sure a decent fix is not possible without some evolution of the language regarding the mutating keyword, so I'm trying to bring this up here in hope of us getting an actual solution. I've changed the title to what I hope is something that better reflects the problem; this thread was originally titled e[</span>swift-evolution] [Bug?] Reference types and mutating methodsf.</div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class=""><br class=""></span></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class=""><br class=""></span></div></div><div class="">PS: I have noticed another side-effect of calling mutating functions on my reference-type collection: it seems to trigger didChange on properties, even when, upon comparing the new and old objects, the reference isn't being changed. I haven't done much experimentation with this behaviour; this may be an unexpected side-effect of an extension method assigning to self, but it feels like it could be undefined behaviour.</div><div class=""><br class="">From James F</div><div class=""><br class="">On 30 Apr 2016, at 16:38, T.J. Usiyan <<a href="mailto:griotspeak@gmail.com" class="">griotspeak@gmail.com</a>> wrote:<br class=""><br class=""></div><blockquote type="cite" class=""><div class=""><div dir="ltr" class="">The problem here seems to be with using the default implementation provided. If you override `append` in ObservedArray, the compiler allows it. That seems 'safe' but odd at first. I wouldn't *want* to implement every mutating method, but that is the current solution. I haven't puzzled out the reasoning behind this myself.<div class=""><br class=""></div><div class=""><br class=""><div class="">``` swift<br class=""><div class="">class ObservedArray<T> : ArrayLiteralConvertible {</div><div class=""> var value: [T]</div><div class=""> init(value: [T]) {</div><div class=""> self.value = value</div><div class=""> }</div><div class=""> required init() {</div><div class=""> self.value = []</div><div class=""> }</div><div class=""><br class=""></div><div class=""> required convenience init(arrayLiteral elements: T...) {</div><div class=""> self.init(elements)</div><div class=""> }</div><div class=""><br class=""></div><div class="">}</div><div class=""><br class=""></div><div class="">extension ObservedArray {</div><div class=""> typealias Index = Int</div><div class=""><br class=""></div><div class=""> var startIndex: Index {</div><div class=""> return value.startIndex</div><div class=""> }</div><div class=""><br class=""></div><div class=""> var endIndex: Index {</div><div class=""> return value.endIndex</div><div class=""> }</div><div class=""><br class=""></div><div class=""> subscript(position: Index) -> T {</div><div class=""> return value[position]</div><div class=""> }</div><div class=""><br class=""></div><div class="">}</div><div class=""><br class=""></div><div class="">extension ObservedArray : RangeReplaceableCollectionType {</div><div class=""> typealias Generator = IndexingGenerator<[T]></div><div class=""><br class=""></div><div class=""> func generate() -> Generator {</div><div class=""> return value.generate()</div><div class=""> }</div><div class="">}</div><div class=""><br class=""></div><div class="">extension ObservedArray {</div><div class=""> func replaceRange<C : CollectionType where C.Generator.Element == Generator.Element>(subRange: Range<Index>, with newElements: C) {</div><div class=""> value.replaceRange(subRange, with: newElements)</div><div class=""> }</div><div class=""><br class=""></div><div class=""> func append(newElement: T) { // <- adding this makes it work</div><div class=""> value.append(newElement)</div><div class=""> }</div><div class="">}</div><div class=""><br class=""></div><div class="">let array: ObservedArray<String> = []</div><div class="">array.append("1")</div><div class=""><br class=""></div><br class="">```</div><div class=""><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><br class=""></div></div></div></div><div class="gmail_extra"><br class=""><div class="gmail_quote">On Sat, Apr 30, 2016 at 7:52 AM, James Froggatt via swift-evolution <span dir="ltr" class=""><<a href="mailto:swift-evolution@swift.org" target="_blank" class="">swift-evolution@swift.org</a>></span> wrote:<br class=""><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">I don't believe this has been addressed, please correct me if I'm wrong.<br class="">
<br class="">
--My Situation--<br class="">
I've recently been working on an observable collection type. Because each stores esubscriptionsf to changes that occur, it made sense to me that this should be a reference type, so subscriptions can't be copied with the values themselves.<br class="">
<br class="">
I have made this class conform to RangeReplaceableCollectionType, providing it with all the standard collection functions. I do the following:<br class="">
<br class="">
let array: ObservedArray<String> = []<br class="">
array.append("1") //Error: Cannot use mutating member on immutable value: earrayf is a eletf constant<br class="">
<br class="">
I have to make the reference immutable just to use my new collection type? This is a bit of a deal-breaker.<br class="">
<br class="">
--The Problem--<br class="">
Mutating methods allow eselff to be reassigned, which is just another way to mutate a value type. However, reassigning eselff has a special meaning to reference types, which is presumably the reason they are disallowed in classes.<br class="">
<br class="">
However, classes can conform to protocols with mutating methods, leading to the compiler disallowing calls to mutating methods for eletf values of type eprotocol<MutatingProtocol, AnyObject>f, which can be an annoyance in generic code. In addition, classes can inherit mutating methods from protocol extensions, leading to the behaviour I describe above.<br class="">
<br class="">
Is this intentional behaviour? Am I going about this in the wrong way? Or is this really an omission in the language?<br class="">
_______________________________________________<br class="">
swift-evolution mailing list<br class="">
<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br class="">
</blockquote></div><br class=""></div>
</div></blockquote></div>_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></div></blockquote></div><br class=""></div></div></div></body></html>