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

Reply via email to