> On Jul 12, 2017, at 3:23 AM, Gor Gyolchanyan via swift-evolution
> <[email protected]> wrote:
>
> Hello, swift community!
>
> Recently I’ve come across a dilemma regarding value-type semantics when
> dealing with generic types.
> Consider a protocol that has a mutating in-place function and a non-mutating
> returning variant of that function:
>
> protocol Transmogrifier {
>
> mutating func transmogrify()
>
> func transmogrified() -> Self
>
> }
>
> One of these methods has to have a default implementation in terms of the
> other.
>
> One way doing it is to implement the mutating version in terms of
> non-mutating because it doesn’t depend on additional conditions to work,
> since assigning to `self` causes a complete copy of the internal state of the
> object regardless of whether it’s a value type or a reference type. However,
> this approach has a big downside: in many cases mutating functions mutate
> only part of the instance, which means that an efficient implementation will
> have to implement the mutating version and because of the way the default
> implementation works, the non-mutating version would also need to be manually
> implemented, which makes the default implementation useless in those cases.
>
> Implementing the non-mutating version in terms of mutating version solves
> this problem nicely, allowing one to focus on mutating only the necessary
> parts of the instance, while leaving the need to return a separate instance
> to the default implementation, which would be perfectly adequate in most
> cases. This approach has its own problem that this pitch seeks to solve. The
> problem becomes apparent when you consider this naive implementation:
>
> extension Transmogrifier {
>
> public func transmogrified() -> Self {
> var result = self
> result.transmogrify()
> return result
> }
>
> }
>
> The above implementation is only correct for value types, because assignment
> is a deep copy. If the instance is of a reference type, the assignment will
> do nothing and the call to the mutating version will apply to the original
> object, violating the postcondition of the function (which states that the
> function shall not modify the instance in any way).
>
> The most straight-forward way of solving this problem is to introduce a new
> protocol for making sure the original instance is always copied:
Immutable types like NSString, NSDictionary etc just return self for the copy.
>
> protocol CopyInitializable {
>
> init(copying other: Self)
>
> }
>
> In which case the default implementation becomes fully correct:
>
> // The `CopyInitializable` conformance can also be moved to the protocol
> itself
> // if the protocol conformance requires value-type semantics.
> extension Transmogrifier where Self: CopyInitializable {
>
> public func transmogrified() -> Self {
> var result = Self(copying: self)
> result.transmogrify()
> return result
> }
>
> }
>
> The downside of this approach is the need to manage CopyInitializable
> conformance of the types that becomes extra hassle that seems to conflict
> with the behavior of value types.
>
> This pitch proposes adding CopyInitializable protocol to the swift standard
> library and having the compiler automatically generate conformance to it for
> all value types.
> This would immediately solve all problems of correct convenient
> implementations of non-mutaiting variants of in-place functions as well as
> remove the hassle of having to manage conformance to CopyInitializable for
> all value types that are guaranteed to have this behavior in the first place.
>
> An good use case would be the NSNumber class, which would conform to
> CopyInitializable and make use of a single obvious mutating-to-nonmutating
> implementation of arithmetic operations that would work equally well on all
> standard numeric types.
NSNumber is an immutable reference type, so copy just returns a strong
reference to itself. So how would a copy initialization of a NSNumber add any
value? If we were to add a copy initializer to NSNumber, it would probably be
implemented as just replacing self in the init with the other object.
For reference types there is already a protocol for what you are attempting to
do; NSCopying (granted it probably should have a Self return instead of Any…
but that is a different can-o-worms).
>
> I’d like to hear opinions regarding this pitch and in case of consensus, I’d
> write an official proposal and offer it for review.
>
> Regards,
> Gor Gyolchanyan.
>
> _______________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution