Sent from my iPad
> On May 4, 2016, at 7:28 AM, T.J. Usiyan via swift-evolution > <[email protected]> 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? One way that this might be workable is if we could overload > protocols for Value vs for reference. > There is a thread going right now discussing adding an intrinsic AnyValue protocol (and a few other intrinsic protocols). I think it is a good idea but I don't believe anyone from the core team has commented in that thread yet. > 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 > > _______________________________________________ > 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
