> 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