[swift-evolution] References in Value Types (Deep-Copy-OnWrite)

ilya ilya.nikokoshev at gmail.com
Thu Dec 17 14:24:19 CST 2015


> Is myVariable's a value type?

It's a struct, so an object that has a copy constuctor that works by
copying a reference. Does it help anyone to call it a "value type"? Maybe
not.

> Is myVariable copy-on-write?

No, copy-on-write refers to the behavior of objects that have a shared
buffer that gets coped lazily, when a mutation is required. This is not the
behavior observed here.

> Should we be able to write to myVariable.value despite reference being
let?

Yes. let a = ... means that a name a is bound to an expression; it doesn't
mean that this name refers to an object with immutable state. Just like we
write

let label = UILabel()

label.text = "Some text"
label.textColor = .redColor()
...

> Is this code unsafe, or misleading? yes. Can we prevent that? maybe

It's very cryptic and mysterious when written with names like MyClass and
MyValue, but is should be more understandiable in a real-world situation:

private class MutableInternalBuffer {
    var value: [Int]
    init(value: [Int]) { self.value = value }
}

struct MutableBufferedData {

    private let internalBuffer: MutableInternalBuffer

    init(initialData value: Int) {
        internalBuffer = InternalBuffer(value: value)
    }

    var currentData: [Int] {
        get { return internalBuffer.value }
        set { internalBuffer.value = newValue }
    }
}

var myMutableData = MutableBufferedData(initialData: [1, 2, 3])
print(myMutableData.value)

let myOtherAccessor = myMutableData

myMutableData.value = [4, 5, 6]
print(myOtherAccessor.currentData)

On Thu, Dec 17, 2015 at 4:51 PM, Andrew Bennett via swift-evolution <
swift-evolution at swift.org> wrote:

> This isn't a proposal, more something to get a discussion started.
>
> TL;DR: You can make a struct look like a class, a class look like a
> struct, a mutable type appear immutable.
>
> I think that Swift is doing these things as it should. However I think
> that there is room for mistakes, and it would be nice to be aware of those
> mistakes and guard against them.
>
> Consider the following code:
>
> private class MyClass {
>
>     var value: Int
>
>     init(value: Int) { self.value = value }
>
> }
>
> struct MyValue {
>
>     private let reference: MyClass
>
>     init(value: Int) {
>
>         self.reference = MyClass(value: value)
>
>     }
>
>     var value: Int {
>
>         get { return self.reference.value }
>
>         set { self.reference.value = newValue }
>
>     }
>
> }
>
>
> var myVariable = MyValue(value: 123)
>
> print(myVariable.value) // "123"
>
>
> let myOtherVariable = myVariable
>
>
> myVariable.value = 456
>
> print(myOtherVariable.value) // "456"!
>
>
> Are all of our assumptions correct?
>  * Is myVariable's a value type? probably
>  * Is myVariable copy-on-write? perhaps
>  * Should we be able to write to myVariable.value despite reference being
> let? probably
>  * Is this code unsafe, or misleading? yes. Can we prevent that? maybe
>
> Some other code, this is not perfect but it mostly makes MyValue
> copy-on-write:
>
> protocol DeepCopier {
>
>     typealias ObjectType: AnyObject
>
>     static func deepCopy(that: ObjectType) -> ObjectType
>
> }
>
> struct UniqueReference<Copier: DeepCopier> {
>
>     private var _reference: Copier.ObjectType
>
>     init(var reference: Copier.ObjectType) {
>
>         if isUniquelyReferencedNonObjC(&reference) {
>
>             _reference = reference
>
>         }
>
>         else {
>
>             _reference = Copier.deepCopy(reference)
>
>         }
>
>     }
>
>     var reference: Copier.ObjectType { return self._reference }
>
>     mutating func update(block: Copier.ObjectType -> Void) {
>
>         if !isUniquelyReferencedNonObjC(&self._reference) {
>
>             self._reference = Copier.deepCopy(self._reference)
>
>         }
>
>         block(self._reference)
>
>     }
>
> }
>
>
> private class MyClass: NonObjectiveCBase {
>
>     var value: Int
>
>     init(value: Int) { self.value = value }
>
>     func deepCopy() -> MyClass {
>
>         return MyClass(value: self.value)
>
>     }
>
>     private struct Copier: DeepCopier {
>
>         static func deepCopy(that: MyClass) -> MyClass {
>
>             return MyClass(value: that.value)
>
>         }
>
>     }
>
> }
>
>
> struct MyValue {
>
>     private var myReference: UniqueReference<MyClass.Copier>
>
>     init(value: Int) {
>
>         self.myReference = UniqueReference(reference: MyClass(value:
> value))
>
>     }
>
>     var value: Int {
>
>         get { return self.myReference.reference.value }
>
>         set { self.myReference.update { $0.value = newValue } }
>
>     }
>
> }
>
>
> var myVariable = MyValue(value: 123)
>
> print(myVariable.value) // "123"
>
>
> let myOtherVariable = myVariable
>
>
> myVariable.value = 456
>
> print(myOtherVariable.value) // "123"
>
> print(myVariable.value) // "456"
>
>
> Even more code, I don't trust anything any more:
>
> struct MyValue {
>
>     init(value: Int) { }
>
>     var value: Int {
>
>         get { return Int(arc4random()) }
>
>         set { }
>
>     }
>
> }
>
>
> The public interface for MyValue in all these cases looks totally innocent:
>
> struct MyValue {
>
>     init(value: Int)
>
>     var value: Int
>
> }
>
>
> Protocols are the same, they provide no guarantees about immutability, or
> deep-copy-on-write:
>
> protocol SomeValue {
>
>     init(value: Int)
>
>     var value: Int { get }
>
> }
>
>
> The results may be a bit surprising.
>
>  * Perhaps we could introduce a "const" keyword like in other languages,
> so the class cannot be mutated (how do we even scope this?).
>  * Perhaps we could prevent structures from holding references (no
> classes, non-pure blocks, no arc4random, no AnySequence, ...).
>  * Perhaps we can add a @copy_on_write keyword on structures that enforces
> certain guarantees? (how?)
>  * Perhaps add language support for DeepCopier (with a better name,
> probably a keyword rather than a protocol), that allows anything to be
> copied like a structure (my preference).
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151217/caaf3297/attachment.html>


More information about the swift-evolution mailing list