References in Value Types (Deep-Copy-OnWrite)


(Andrew Bennett) #1

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).


(Brent Royal-Gordon) #2

TL;DR: You can make a struct look like a class, a class look like a struct, a mutable type appear immutable.

This is simply not possible to prevent, at least without turning structs into dumb data structures a la C. Here’s something that’s value types all the way down, but behaves like a reference type:
  
  struct MyValue {
      private static var realValues: [Int] = []
      private let index: Int

      init(value: Int) {
          self.index = MyValue.realValues.count
          MyValue.realValues.append(value)
      }
      var value: Int {
          get { return MyValue.realValues[index] }
          set { MyValue.realValues[index] = newValue }
      }
  }

Similarly, you can make reference types with value-like semantics—think of NSDate, which is a reference type, but is immutable and only provides operations which return a new instance. The ultimate proof of this is not in Swift but in Ruby, where even simple numbers are objects, but they offer no mutation operations so they behave just like value-typed numbers in other languages.

Ultimately, you just have to trust the person writing the type to provide the expected semantics, or to document any deviations. (GeneratorType, which does not require even struct-based generators to guarantee that they will iterate separately if you copy them, are an example of the latter.) This leaves room for mischief, but that’s life in a Turing machine.

···

--
Brent Royal-Gordon
Architechies


(ilya) #3

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


(Andrew Bennett) #4

I agree with pretty much everything you said, although I'm not saying we
should "solve" this, but that we should discuss it. One thing we might be
able to do is instead of trusting the person writing the type to:

   - "provide the expected semantics, or to document any deviations"

we may trust the person writing the type to:

   - "annotate the type with the expected semantics"

If such annotations existed. It wouldn't force everything to be c-like, but
it could allow them to be predictable.

I'm not saying annotations are the way to go, just that there is an issue,
you seem to agree, and that it'd be nice to brainstorm it.

···

On Fri, Dec 18, 2015 at 1:42 AM, Brent Royal-Gordon <brent@architechies.com> wrote:

> TL;DR: You can make a struct look like a class, a class look like a
struct, a mutable type appear immutable.

This is simply not possible to prevent, at least without turning structs
into dumb data structures a la C. Here’s something that’s value types all
the way down, but behaves like a reference type:

        struct MyValue {
            private static var realValues: [Int] = []
            private let index: Int

            init(value: Int) {
                self.index = MyValue.realValues.count
                MyValue.realValues.append(value)
            }
            var value: Int {
                get { return MyValue.realValues[index] }
                set { MyValue.realValues[index] = newValue }
            }
        }

Similarly, you can make reference types with value-like semantics—think of
NSDate, which is a reference type, but is immutable and only provides
operations which return a new instance. The ultimate proof of this is not
in Swift but in Ruby, where even simple numbers are objects, but they offer
no mutation operations so they behave just like value-typed numbers in
other languages.

Ultimately, you just have to trust the person writing the type to provide
the expected semantics, or to document any deviations. (GeneratorType,
which does not require even struct-based generators to guarantee that they
will iterate separately if you copy them, are an example of the latter.)
This leaves room for mischief, but that’s life in a Turing machine.

--
Brent Royal-Gordon
Architechies