[Bug?] Reference types and mutating methods


(James F) #1

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?


(TJ Usiyan) #2

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.

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 < swift-evolution@swift.org> 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
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(James F) #3

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 <griotspeak@gmail.com> 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.

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 <swift-evolution@swift.org> 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
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jordan Rose) #4

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 <swift-evolution@swift.org> 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 <griotspeak@gmail.com <mailto:griotspeak@gmail.com>> 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.

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 <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> 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
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(TJ Usiyan) #5

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.

TJ

···

On Tue, May 3, 2016 at 11:02 PM, Jordan Rose <jordan_rose@apple.com> 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 < > swift-evolution@swift.org> 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 <griotspeak@gmail.com> 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.

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 < > swift-evolution@swift.org> 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
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(James F) #6

Thanks. It's a shame this isn't being addressed. I find myself always considering whether things are value-type or reference-type when writing generic code, avoiding assignments to self for this reason, and feel bad adding mutating methods to protocols knowing that it will make the protocol almost unusable for classes. Maybe I'm the exception?

I think that if we had an AnyValue protocol, a Copyable protocol could provide value-types a default implementation which returns self, and this could be used when copying is needed. It would at least be better than restricting protocols to AnyValue. It seems it would work for your ‘removing’ example, even if it doesn't solve the underlying problem.

From James F

···

On 4 May 2016, at 04:02, Jordan Rose <jordan_rose@apple.com> 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 <swift-evolution@swift.org> 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 <griotspeak@gmail.com> 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.

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 <swift-evolution@swift.org> 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
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joe Groff) #7

Something similar to the "classes can't conform to protocols with mutating requirements" restriction would fall out if we had a compiler-enforced notion of pure functions. A "pure" mutating method would only be allowed to mutate its `self` value; since a class reference's referenced instance is not part of the reference value, it wouldn't be possible for a class method that mutates the referenced instance to satisfy a pure mutating requirement.

-Joe

···

On May 3, 2016, at 8:02 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org> 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.


(Matthew Johnson) #8

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.

···

Sent from my iPad

On May 4, 2016, at 7:28 AM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

TJ

On Tue, May 3, 2016 at 11:02 PM, Jordan Rose <jordan_rose@apple.com> 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 <swift-evolution@swift.org> 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 <griotspeak@gmail.com> 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.

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 <swift-evolution@swift.org> 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
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joe Groff) #9

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.

I'm not totally comfortable with this, because references are a kind of value. I can see value in there being some kind of PureValue protocol, for types that represent fully self-contained values, but conforming to that protocol requires a bit more thought than just being a struct or enum, since there are structs that have reference semantics (such as UnsafePointer), and there are hybrid value types that contain references to data that isn't part of the value (an Array<Class>, for instance).

-Joe

···

On May 4, 2016, at 5:28 AM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

TJ

On Tue, May 3, 2016 at 11:02 PM, Jordan Rose <jordan_rose@apple.com> 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 <swift-evolution@swift.org> 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 <griotspeak@gmail.com> 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.

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 <swift-evolution@swift.org> 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
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(James F) #10

Your comment on overloads for reference types gave me an idea. I've just tried in a playground, and overloading is currently possible for protocol extensions. Providing an implementation, then separate ones in an extension where Self: AnyObject, will cause the compiler to choose the more specific implementation, which is allowed to omit the mutating keyword. This does work using a generic variable of type protocol<P, AnyObject>.

However, whether the value conforms to AnyObject has to be known at compile time, otherwise the more general implementation will (invisibly to the programmer) be called instead, so generic code needs to explicitly attempt a cast to protocol<P, AnyObject>. Additionally, using this as a general fix would require large amounts of copy & pasting implementations to duplicate every function within a ‘where Self: AnyObject’ extension.

So by itself, not a great solution either. If I understand what you're suggesting, overloading protocols (one for AnyValue, one for AnyObject) would require the method duplication and casts, and remove confusion for users, preventing truly generic code. I'm divided as to whether it's worth the headaches, though.

From James F

···

On 4 May 2016, at 13:28, T.J. Usiyan <griotspeak@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? 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 <jordan_rose@apple.com> 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 <swift-evolution@swift.org> 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 <griotspeak@gmail.com> 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.

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 <swift-evolution@swift.org> 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
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Dave Abrahams) #11

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.

···

on Wed May 04 2016, "T.J. Usiyan" <griotspeak-AT-gmail.com> wrote:

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 <jordan_rose@apple.com> 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 > <swift-evolution@swift.org> 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 <griotspeak@gmail.com> 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 > <swift-evolution@swift.org> 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,
                >’, 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
                swift-evolution@swift.org
                https://lists.swift.org/mailman/listinfo/swift-evolution

        _______________________________________________
        swift-evolution mailing list
        swift-evolution@swift.org
        https://lists.swift.org/mailman/listinfo/swift-evolution

--
Dave


(David Sweeris) #12

+1

I'd think that both "PureValue" and "PureReference" would be useful. Isn't it the mixed types that make for tricky mutation rules & serialization?

···

On May 4, 2016, at 11:12, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

I can see value in there being some kind of PureValue protocol, for types that represent fully self-contained values, but conforming to that protocol requires a bit more thought than just being a struct or enum, since there are structs that have reference semantics (such as UnsafePointer), and there are hybrid value types that contain references to data that isn't part of the value (an Array<Class>, for instance).

-Joe


(Dave Abrahams) #13

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.

I'm not totally comfortable with this, because references are a kind
of value.

We're using the word “value” in 3 different ways now. If we don't sort
them out, this is going to become impossible to resolve. So let me
propose some terms:

1. Joe's sense, i.e. the compiler-implementation-level sense, in which a
   value of type T is essentially what an UnsafePointer<T> points at.
   In this sense, a variable of any type T “has a value.” Even though
   it's not strictly correct (since the term really applies to
   expressions), I propose “rvalue” for this one.

2. The “value types vs reference types” sense, where every type falls
   into one of two crisp buckets based on how it's declared. I propose
   we always say “reference type” or “value type” for this one.

3. The “value semantics vs reference semantics” sense, where a type's
   category depends on how it is implemented, and it's possible (though
   inadvisable) to fall outside either bucket. This is the only
   interesting category when you're discussing protocols and
   constraints, and doesn't have any intrinsic relationship to sense
   #2. I propose we always say “value semantics” or “reference
   semantics” for this one.

···

on Wed May 04 2016, Joe Groff <jgroff-AT-apple.com> wrote:

On May 4, 2016, at 5:28 AM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

I can see value in there being some kind of PureValue protocol, for
types that represent fully self-contained values, but conforming to
that protocol requires a bit more thought than just being a struct or
enum, since there are structs that have reference semantics (such as
UnsafePointer), and there are hybrid value types that contain
references to data that isn't part of the value (an Array<Class>, for
instance).

-Joe

TJ

On Tue, May 3, 2016 at 11:02 PM, Jordan Rose <jordan_rose@apple.com> 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 <swift-evolution@swift.org> 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 <griotspeak@gmail.com> 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.

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 <swift-evolution@swift.org> 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
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Dave


(Matthew Johnson) #14

I can see value in there being some kind of PureValue protocol, for types that represent fully self-contained values, but conforming to that protocol requires a bit more thought than just being a struct or enum, since there are structs that have reference semantics (such as UnsafePointer), and there are hybrid value types that contain references to data that isn't part of the value (an Array<Class>, for instance).

-Joe

+1

I'd think that both "PureValue" and "PureReference" would be useful. Isn't it the mixed types that make for tricky mutation rules & serialization?

I also like Joe’s idea. It fits with the direction in Swift of attaching semantics, not just syntax, to protocols.

I was thinking of something pretty similar to your PureReference idea, but slightly different. Pure / immutable references have value semantics. With that in mind I was thinking of an ImmutableObject protocol which would inherit from both AnyObject and PureValue.

···

On May 4, 2016, at 1:21 PM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

On May 4, 2016, at 11:12, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joe Groff) #15

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.

I'm not totally comfortable with this, because references are a kind
of value.

We're using the word “value” in 3 different ways now. If we don't sort
them out, this is going to become impossible to resolve. So let me
propose some terms:

1. Joe's sense, i.e. the compiler-implementation-level sense, in which a
  value of type T is essentially what an UnsafePointer<T> points at.
  In this sense, a variable of any type T “has a value.” Even though
  it's not strictly correct (since the term really applies to
  expressions), I propose “rvalue” for this one.

2. The “value types vs reference types” sense, where every type falls
  into one of two crisp buckets based on how it's declared. I propose
  we always say “reference type” or “value type” for this one.

3. The “value semantics vs reference semantics” sense, where a type's
  category depends on how it is implemented, and it's possible (though
  inadvisable) to fall outside either bucket. This is the only
  interesting category when you're discussing protocols and
  constraints, and doesn't have any intrinsic relationship to sense
  #2. I propose we always say “value semantics” or “reference
  semantics” for this one.

I claim that my sense is the same as #2. A reference has all the properties of a value-type value; it has the additional ability to be used as a handle to access independent program state. Int isn't any less of a value because it can be used to reference values in an array. Rather than try to break the world down according to categories of types, I think it's more effective to look at the semantics of *operations*. So called "reference types" have value semantics operations too; reassigning a class reference, '+='-ing a pointer, and adding or removing elements from an Array of references are all operations that mutate only the value being operated on, even though none of these types are strictly "value types".

-Joe

···

On May 5, 2016, at 8:56 AM, Dave Abrahams <dabrahams@apple.com> wrote:
on Wed May 04 2016, Joe Groff <jgroff-AT-apple.com> wrote:

On May 4, 2016, at 5:28 AM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

I can see value in there being some kind of PureValue protocol, for
types that represent fully self-contained values, but conforming to
that protocol requires a bit more thought than just being a struct or
enum, since there are structs that have reference semantics (such as
UnsafePointer), and there are hybrid value types that contain
references to data that isn't part of the value (an Array<Class>, for
instance).

-Joe

TJ

On Tue, May 3, 2016 at 11:02 PM, Jordan Rose <jordan_rose@apple.com> 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 <swift-evolution@swift.org> 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 <griotspeak@gmail.com> 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.

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 <swift-evolution@swift.org> 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
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Dave


(David Sweeris) #16

Having given it some more thought... Does "PureReference" make sense? What would it mean? At some point a reference has to, you know, actually refer to a concrete value. Otherwise it's just turtles all the way down.

···

Sent from my iPhone

On May 4, 2016, at 13:32, Matthew Johnson <matthew@anandabits.com> wrote:

On May 4, 2016, at 1:21 PM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

On May 4, 2016, at 11:12, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

I can see value in there being some kind of PureValue protocol, for types that represent fully self-contained values, but conforming to that protocol requires a bit more thought than just being a struct or enum, since there are structs that have reference semantics (such as UnsafePointer), and there are hybrid value types that contain references to data that isn't part of the value (an Array<Class>, for instance).

-Joe

+1

I'd think that both "PureValue" and "PureReference" would be useful. Isn't it the mixed types that make for tricky mutation rules & serialization?

I also like Joe’s idea. It fits with the direction in Swift of attaching semantics, not just syntax, to protocols.

I was thinking of something pretty similar to your PureReference idea, but slightly different. Pure / immutable references have value semantics. With that in mind I was thinking of an ImmutableObject protocol which would inherit from both AnyObject and PureValue.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Dave Abrahams) #17

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.

I'm not totally comfortable with this, because references are a kind
of value.

We're using the word “value” in 3 different ways now. If we don't sort
them out, this is going to become impossible to resolve. So let me
propose some terms:

1. Joe's sense, i.e. the compiler-implementation-level sense, in which a
  value of type T is essentially what an UnsafePointer<T> points at.
  In this sense, a variable of any type T “has a value.” Even though
  it's not strictly correct (since the term really applies to
  expressions), I propose “rvalue” for this one.

2. The “value types vs reference types” sense, where every type falls
  into one of two crisp buckets based on how it's declared. I propose
  we always say “reference type” or “value type” for this one.

3. The “value semantics vs reference semantics” sense, where a type's
  category depends on how it is implemented, and it's possible (though
  inadvisable) to fall outside either bucket. This is the only
  interesting category when you're discussing protocols and
  constraints, and doesn't have any intrinsic relationship to sense
  #2. I propose we always say “value semantics” or “reference
  semantics” for this one.

I claim that my sense is the same as #2. A reference has all the
properties of a value-type value; it has the additional ability to be
used as a handle to access independent program state. Int isn't any
less of a value because it can be used to reference values in an
array.

It's less of a value in sense #2 because

  x as Any as? AnyObject

is always nil for a value in sense #2.

Rather than try to break the world down according to categories of
types, I think it's more effective to look at the semantics of
*operations*. So called "reference types" have value semantics
operations too;

Please define the term “value semantics operation.” I wager there is no
such thing.

reassigning a class reference, '+='-ing a pointer,

A pointer is a value type in every sense of the word.

and adding or removing elements from an Array of references are all
operations that mutate only the value being operated on, even though
none of these types are strictly "value types".

Now you're really blending meanings, or at least being very fuzzy. Your
Array of references has a sense #1 value that might not even be altered
when you change the elements in the array. It is a sense #2 value by
language rules. It almost has sense #3 value semantics but for our
inability to compare it with "==", because we don't implicitly get "=="
defined in terms of "===" for all classes.

Saying something has value semantics is meaningless without a way to
determine equivalence that respects the boundaries of the value. The
*whole point* of value semantics is that initialization, assignment,
parameter passing, and returning all create *independent* instances, with
no observable shared state, and that can be read and mutated
concurrently in different threads. To even test that, you need to nail
down what it means for a value to change, and for that, you need an
equality comparison.

···

on Thu May 05 2016, Joe Groff <jgroff-AT-apple.com> wrote:

On May 5, 2016, at 8:56 AM, Dave Abrahams <dabrahams@apple.com> wrote:
on Wed May 04 2016, Joe Groff <jgroff-AT-apple.com> wrote:

On May 4, 2016, at 5:28 AM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

-Joe

I can see value in there being some kind of PureValue protocol, for
types that represent fully self-contained values, but conforming to
that protocol requires a bit more thought than just being a struct or
enum, since there are structs that have reference semantics (such as
UnsafePointer), and there are hybrid value types that contain
references to data that isn't part of the value (an Array<Class>, for
instance).

-Joe

TJ

On Tue, May 3, 2016 at 11:02 PM, Jordan Rose <jordan_rose@apple.com> 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 <swift-evolution@swift.org> 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 <griotspeak@gmail.com> 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.

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 <swift-evolution@swift.org> 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
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Dave

--
Dave


(Matthew Johnson) #18

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.

I'm not totally comfortable with this, because references are a kind
of value.

We're using the word “value” in 3 different ways now. If we don't sort
them out, this is going to become impossible to resolve. So let me
propose some terms:

1. Joe's sense, i.e. the compiler-implementation-level sense, in which a
value of type T is essentially what an UnsafePointer<T> points at.
In this sense, a variable of any type T “has a value.” Even though
it's not strictly correct (since the term really applies to
expressions), I propose “rvalue” for this one.

2. The “value types vs reference types” sense, where every type falls
into one of two crisp buckets based on how it's declared. I propose
we always say “reference type” or “value type” for this one.

3. The “value semantics vs reference semantics” sense, where a type's
category depends on how it is implemented, and it's possible (though
inadvisable) to fall outside either bucket. This is the only
interesting category when you're discussing protocols and
constraints, and doesn't have any intrinsic relationship to sense
#2. I propose we always say “value semantics” or “reference
semantics” for this one.

I claim that my sense is the same as #2. A reference has all the properties of a value-type value; it has the additional ability to be used as a handle to access independent program state. Int isn't any less of a value because it can be used to reference values in an array. Rather than try to break the world down according to categories of types, I think it's more effective to look at the semantics of *operations*. So called "reference types" have value semantics operations too; reassigning a class reference, '+='-ing a pointer, and adding or removing elements from an Array of references are all operations that mutate only the value being operated on, even though none of these types are strictly "value types”.

The distinction between “reference as a value” and “value the reference points to” is a pretty important one. All of the operations you discuss are really operations on the “reference as a value”.

I think the reason we talk about *types* rather than *operations* having value semantics is because that tells us that *all* operations on any instance are going to have value semantics. This means that *nobody else* is going to mutate the variable in question behind our back. If the type has “reference semantic operations” we don’t have that guarantee today. The fact that the type may also have some "value semantic operations" seems relatively unimportant.

One possible avenue for improvement in the language where this distinction could be useful is by allowing us to specify which operations have "value semantics" on reference types and allow us to designate specific *references* as being value semantic. `let` wouldn’t work as that only means that the reference itself is immutable, not the value it refers to. We would need some additional syntax to specify that the value the reference points to cannot be mutated. Such a reference could only be assigned to when the compiler can prove it will refer to a value that has *no* existing mutable references. This would allow us to capture the benefits of value semantics for specific *instances* of a reference type.

-Matthew

···

On May 5, 2016, at 11:06 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On May 5, 2016, at 8:56 AM, Dave Abrahams <dabrahams@apple.com> wrote:
on Wed May 04 2016, Joe Groff <jgroff-AT-apple.com> wrote:

On May 4, 2016, at 5:28 AM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

-Joe

I can see value in there being some kind of PureValue protocol, for
types that represent fully self-contained values, but conforming to
that protocol requires a bit more thought than just being a struct or
enum, since there are structs that have reference semantics (such as
UnsafePointer), and there are hybrid value types that contain
references to data that isn't part of the value (an Array<Class>, for
instance).

-Joe

TJ

On Tue, May 3, 2016 at 11:02 PM, Jordan Rose <jordan_rose@apple.com> 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 <swift-evolution@swift.org> 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 <griotspeak@gmail.com> 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.

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 <swift-evolution@swift.org> 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
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Sweeris) #19

... And 30 seconds later I realized that "PureReference" could be fulfilled by something with only static or computed properties.

···

Sent from my iPhone

On May 4, 2016, at 16:16, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

Having given it some more thought... Does "PureReference" make sense? What would it mean? At some point a reference has to, you know, actually refer to a concrete value. Otherwise it's just turtles all the way down.

Sent from my iPhone

On May 4, 2016, at 13:32, Matthew Johnson <matthew@anandabits.com> wrote:

On May 4, 2016, at 1:21 PM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

On May 4, 2016, at 11:12, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

I can see value in there being some kind of PureValue protocol, for types that represent fully self-contained values, but conforming to that protocol requires a bit more thought than just being a struct or enum, since there are structs that have reference semantics (such as UnsafePointer), and there are hybrid value types that contain references to data that isn't part of the value (an Array<Class>, for instance).

-Joe

+1

I'd think that both "PureValue" and "PureReference" would be useful. Isn't it the mixed types that make for tricky mutation rules & serialization?

I also like Joe’s idea. It fits with the direction in Swift of attaching semantics, not just syntax, to protocols.

I was thinking of something pretty similar to your PureReference idea, but slightly different. Pure / immutable references have value semantics. With that in mind I was thinking of an ImmutableObject protocol which would inherit from both AnyObject and PureValue.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #20

Having given it some more thought... Does "PureReference" make sense? What would it mean? At some point a reference has to, you know, actually refer to a concrete value. Otherwise it's just turtles all the way down.

In my thinking PureReference (or ImmutableObject) has semantics indicating that the type is a value-semantic reference type. I think this could be quite useful in some cases.

···

On May 4, 2016, at 4:16 PM, David Sweeris <davesweeris@mac.com> wrote:

Sent from my iPhone

On May 4, 2016, at 13:32, Matthew Johnson <matthew@anandabits.com> wrote:

On May 4, 2016, at 1:21 PM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

On May 4, 2016, at 11:12, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

I can see value in there being some kind of PureValue protocol, for types that represent fully self-contained values, but conforming to that protocol requires a bit more thought than just being a struct or enum, since there are structs that have reference semantics (such as UnsafePointer), and there are hybrid value types that contain references to data that isn't part of the value (an Array<Class>, for instance).

-Joe

+1

I'd think that both "PureValue" and "PureReference" would be useful. Isn't it the mixed types that make for tricky mutation rules & serialization?

I also like Joe’s idea. It fits with the direction in Swift of attaching semantics, not just syntax, to protocols.

I was thinking of something pretty similar to your PureReference idea, but slightly different. Pure / immutable references have value semantics. With that in mind I was thinking of an ImmutableObject protocol which would inherit from both AnyObject and PureValue.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution