on Wed May 04 2016, "T.J. Usiyan" <griotspeak-AT-gmail.com> wrote:
> Something about your first paragraph reminded me of a question I've had for a > while. Is there a reasoning behind not being able to restrict a protocol to > value types? There are two answers: 1. We haven't gotten around to it 2. As I mentioned elsewhere, that doesn't really have any meaning unless we also add some semantic restrictions, because a “value type” can have reference semantics. > One way that this might be workable is if we could overload protocols > for Value vs for reference. > > TJ > > On Tue, May 3, 2016 at 11:02 PM, Jordan Rose <[email protected]> wrote: > > 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 it’s very hard to write an algorithm > that’s correct for both. > > func removing(_ element: Element) -> Self { > var result = self // not necessarily a copy… > result.remove(element) > return result // not necessarily an independent value > } > > func zapBadElements<C: RangeReplaceableCollection where > C.Generator.Element == Int>(_ nums: inout C) { > // requires inout on ‘nums’ even when it’s a class > for i in nums.indices { > if nums[i] < 0 { > nums.removeAtIndex(i) > } > } > // …because of this. > if nums.lazy.filter { $0 == 0 }.count > 5 { > nums = C() > } > } > > var refCollection: SharedArrayOfSomeKind<Int> = … > // either the variable ‘refCollection’ or the instance of > ‘SharedArrayOfSomeKind’ might be mutated…or both! > zapBadElements(&refCollection) > > There are of course ways to safely use a protocol with mutating > requirements > with classes, namely if you only use them for mutation (i.e. they’re only > called from ‘mutating’ members or on ‘inout’ parameters) and never rely on > value copying (no assignment, no returning). Most simple wrappers around > mutating members would fall into this category. > > We didn’t really develop the idea very far yet because there’s been more > pressing things to worry about. I’m bringing it up here because it’s an > important idea that shouldn’t get lost. > > --- > > In lieu of this, I and a few others brought up the “incorrect” behavior of > reassigning ‘self’ in a protocol extension when the model type is a class, > and got shot down. I don’t 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 it’s because it > means things like zapBadElements are more likely to work correctly^W as > expected―if you don’t have any other references at the time you do the > mutation, it can work. But yeah, I’m uncomfortable with the situation > we’re > in right now. > > Jordan > > On May 3, 2016, at 13:09, James Froggatt via swift-evolution > <[email protected]> wrote: > > 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. > > 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 ‘let’ when calling these functions, and allows methods to > reassign the reference ‘self’ to a new object. The best solution is to > manually implement each method, removing the mutating modifier, yet > this > workaround doesn't extend to generic code. > > To fix this behaviour, we would need to distinguish between ‘true’ > mutating functions, which reassign self, and ‘partially’ mutating > functions, for use in generics and protocol extensions, which can > reassign properties only. > > Is there any support for making this change? Or are there any simpler > solutions? > > 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 > ‘[swift-evolution] [Bug?] Reference types and mutating methods’. > > 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. > > From James F > > On 30 Apr 2016, at 16:38, T.J. Usiyan <[email protected]> wrote: > > 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. > > ``` swift > > class ObservedArray<T> : ArrayLiteralConvertible { > var value: [T] > init(value: [T]) { > self.value = value > } > required init() { > self.value = [] > } > > required convenience init(arrayLiteral elements: T...) { > self.init(elements) > } > > } > > extension ObservedArray { > typealias Index = Int > > var startIndex: Index { > return value.startIndex > } > > var endIndex: Index { > return value.endIndex > } > > subscript(position: Index) -> T { > return value[position] > } > > } > > extension ObservedArray : RangeReplaceableCollectionType { > typealias Generator = IndexingGenerator<[T]> > > func generate() -> Generator { > return value.generate() > } > } > > extension ObservedArray { > func replaceRange<C : CollectionType where C.Generator.Element == > Generator.Element>(subRange: Range<Index>, with newElements: C) { > value.replaceRange(subRange, with: newElements) > } > > func append(newElement: T) { // <- adding this makes it work > value.append(newElement) > } > } > > let array: ObservedArray<String> = [] > array.append("1") > > ``` > > On Sat, Apr 30, 2016 at 7:52 AM, James Froggatt via > swift-evolution > <[email protected]> wrote: > > I don't believe this has been addressed, please correct me if I'm > wrong. > > --My Situation-- > I've recently been working on an observable collection type. > Because each stores ‘subscriptions’ 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. > > I have made this class conform to > RangeReplaceableCollectionType, providing it with all the > standard collection functions. I do the following: > > let array: ObservedArray<String> = [] > array.append("1") //Error: Cannot use mutating member on > immutable value: ‘array’ is a ‘let’ constant > > I have to make the reference immutable just to use my new > collection type? This is a bit of a deal-breaker. > > --The Problem-- > Mutating methods allow ‘self’ to be reassigned, which is just > another way to mutate a value type. However, reassigning > ‘self’ > has a special meaning to reference types, which is presumably > the reason they are disallowed in classes. > > However, classes can conform to protocols with mutating > methods, > leading to the compiler disallowing calls to mutating methods > for ‘let’ values of type ‘protocol<MutatingProtocol, > AnyObject>’, 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. > > Is this intentional behaviour? Am I going about this in the > wrong way? Or is this really an omission in the language? > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution > > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution > -- Dave _______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
