> On 7 Mar 2017, at 02:12, Karl Wagner <[email protected]> wrote:
> 
> 
>> On 6 Mar 2017, at 22:30, Dave Abrahams via swift-evolution 
>> <[email protected]> wrote:
>> 
>> 
>> on Mon Mar 06 2017, John McCall <[email protected]> wrote:
>> 
>>>> On Mar 6, 2017, at 3:46 PM, Karl Wagner 
>>>> <[email protected]> wrote:
>>>> Thanks very much for writing this up, it’s very interesting.
>>>> 
>>>> The one thing which I think needs improvement is the Copyable protocol. I 
>>>> think that this is
>>> actually part of a larger problem in Swift, which is that we don’t expose 
>>> enough information to
>>> generic code for it to operate safely. This goes back to people asking for 
>>> a value-equivalent to the
>>> “class” or “AnyObject” magic protocols.
>>>> 
>>> 
>>>> For example, often you would like to wrap a Collection and keep some 
>>>> information about what it contains. In order to do that generically, you 
>>>> need to ensure a client can hand you an exclusive view of a collection’s 
>>>> contents, so you know they will not mutate underneath your feet. 
>>>> 
>>>> Currently, that means you need a RangeReplaceableCollection because it 
>>>> includes an empty initialiser which allows you to create a unique copy of 
>>>> the Collection’s contents. It’s a workaround, but it means we have this 
>>>> unnecessary protocol requirement in the standard library, and in the 
>>>> implementation, which is making copies that may not be required. If my 
>>>> CollectionWrapper is initialised with something which has ValueSemantics, 
>>>> I don’t need to create a whole new instance with equal contents to ensure 
>>>> exclusivity of those contents. If this was an Array, for example, I could 
>>>> simply assign it and it would ensure the underlying contiguous buffer is 
>>>> never mutated.
>>>> 
>>>> struct CollectionWrapper<C: Collection> {
>>>>   var _underlying: C
>>>> 
>>>>   init(contentsOf other: C) where C: RangeReplaceableCollection { 
>>>>       _underlying = C();
>>>>       
>>>> _underlying.replaceSubrange(_underlying.startIndex..<_underlying.endIndex, 
>>>> with: other) 
>>>>   }
>>>> }
>>>> 
>>>> // Would love to do this…
>>>> 
>>>> extension CollectionWrapper {
>>>>   init(contentsOf other: C) where C: ValueSemantics { 
>>>>       _underlying = other 
>>>>   }
>>>> }
>>>> 
>>>> As the proposal notes (with the File example), these are semantic
>>>> considerations which go beyond simple struct/class distinctions:
>>>> structs may have reference semantics, and classes may have value
>>>> semantics. Would it be possible to model Copyable/move-only in terms
>>>> of two new protocols, ValueSemantics and ReferenceSemantics, with
>>>> trivial types/non-trivial types given appropriate defaults
>>>> (overridable by the programmer, such as for “File”)?
>>> 
>>> Class types have reference semantics and are still copyable; ownership
>>> is not going to change that.  More generally, I don't see how anything
>>> we can do in ownership could ever do something like eliminate the
>>> possibility of a reference-semantics collection from the language.
>> 
>> I think it might not be very related to ownership, but the use of the
>> word “Copyable” may be a problem.  For most programmers when applied to
>> reference types “Copyable” will connote “Clonable,” i.e. that the
>> referent (rather than the reference, which is what you're referring to
>> when you say class types are copyable) can be explicitly copied.  So I'm
>> betting Karl read “Copyable” and was led to this other topic which is of
>> concern to many people.  In that sense, claiming “Copyable” for
>> ownership without also addressing “Clonable” could be a problem.
>> 
>> The idea behind “Clonable” would be that it gives you a *generic* way to
>> create a logically *independent* copy of a value, that would work both
>> for value types and reference types.
>> 
>> -- 
>> -Dave
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> [email protected]
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 
> Part of it is the name; partly I was led there by the examples in the 
> document. For example, a move-only struct with a deinitialiser looks an awful 
> lot like it’s trying to express ReferenceSemantics, since it infers identity. 
> How would using a move-only struct differ from using a class?
> 
> I’ve been thinking that we could do with a collection of magic protocols to 
> constrain generic code based on type layout in general. There is a lot of 
> generic code which could benefit from optimised implementations if they know 
> that T is trivial, for example. At a higher level, we often want to know 
> whether a particular type (regardless of struct/class) has reference/value 
> semantics because we care about exclusivity over its contents.
> 
> Some of the constraints of the “law of exclusivity” sound like they are 
> providing a kind of value semantic of contents at the variable-level (e.g. 
> shared references are allowed as long as they do not change the contents). 
> Several of the concepts in the document are new to me, but at some broad 
> level there appear to be conceptual similarities. 
> 
> At the same time, while references to classes are “Copyable” in the ownership 
> sense, those copies are very different from copies of structs. For classes, 
> those copies are basically worthless to the optimiser because it can’t 
> guarantee anything about who else has references to the instance. I’m not 
> really sure classes actually benefit at all from being “Copyable”. Perhaps 
> they should be some other, closely-related thing instead?
> 
> - Karl
> 
> 

To put this in code:

magic-protocol HasReferenceSemantics { func retain() }
magic-protocol MoveOnly: HasReferenceSemantics {}

magic-protocol HasValueSemantics {}
magic-protocol Copyable: HasValueSemantics { func copy() -> Self }

extension MoveOnly {
mutating func move() -> Self {
    defer { self.deinitialise() }
    return self 
}
}

imaginary-struct Reference<To: HasReferenceSemantics>: HasValueSemantics {
    let referencedThing: To

    init(_ p: inout To) where To: MoveOnly {
        referencedThing = p.move()
    }
    init(_ p: To) {
        p.retain()
        referencedThing = p
    }
    func copy() -> Self {
       referencedThing.retain()
       return self
    }
}

imaginary-class Shared<Thing: HasValueSemanics>: HasReferenceSemantics {
    let sharedThing: Thing
    func retain() { /* no-op */ }
}

That’s basically my understanding from the document. Is that more-or-less 
correct?

- Karl





_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to