> On May 20, 2016, at 8:53 PM, Brent Royal-Gordon <[email protected]> 
> wrote:
> 
>> You can implement reference types with value semantics and value types with 
>> reference semantics.
> 
>> Immutable reference types actually *can* provide valid value semantics (at 
>> least as long as as they can provide their own implementation of `==` which 
>> I believe Dave A is arguing against).
> 
> Not in the sense I mean—that is, not if the protocol has members which 
> require you to mutate self.

Yes, it goes without saying that an immutable reference type cannot have a 
mutating member.

> 
> If a reference type is immutable, you can treat it like a value type. But 
> then it can't correctly conform to a protocol (like 
> `RangeReplaceableCollection`) with mutating members, because it is 
> *immutable*. It would have to take `self` as an `inout` parameter and replace 
> `self` with a copy, but methods on classes cannot take `self` as an inout 
> parameter. It's stuck.

It’s not just for that reason alone.  An immutable type would not be able to 
support operations that mutate, regardless of the need to take `self` as an 
`inout`parameter.  For example, if there were `NSImmutableArray` (`NSArray` 
does not guarantee immutability, it just doesn’t expose mutating operations) it 
would not be able to conform to `RangeReplaceableCollection` most importantly 
because you could not do things like `replaceSubrange`.

> 
> In other words, there are three sensible types of protocols:
> 
> 1. Protocols that do not require any self-mutating members, and do not assume 
> either value or reference semantics.
> 2. Protocols that require self-mutating members, and assume value semantics.
> 3. Protocols that require self-mutating members, and assume reference 
> semantics.

It is also sensible to have a protocol that requires value semantics even if it 
does not have mutating operations.  There are times when you want a guarantee 
that you will not observe mutations made by others.

> 
> However, what Swift actually supports is: 
> 
> 1. Protocols that do not require any self-mutating members, and do not assume 
> either value or reference semantics.
> 2. Protocols that require self-mutating members, and can be implemented by 
> either classes or structs/enums.
> 3. Protocols that require self-mutating members, and must be implemented by 
> classes.
> 
> The mismatch in #2 (between assuming value semantics and allowing classes to 
> conform) is one source of mischief: the compiler does nothing to help you 
> realize that you cannot possibly correctly conform a class to the protocol. 
> Requiring you to use a struct or an enum is not *sufficient* to ensure you'll 
> provide value semantics, but it is *necessary* in the face of self-mutation.
> 
> The mismatch in #3 (between assuming reference semantics and requiring a 
> class) is another, separate source of mischief: your particular value type 
> happens to provide reference semantics even in the face of self-mutation, but 
> it can't conform to a class-constrained protocol. This *is* a problem, but I 
> consider it less important because you can always wrap your value type in a 
> class to convince the compiler you know what you're doing.
> 
> In #2, the compiler is not being cautious enough; in #3, it's being too 
> cautious. You can work around #3, but there's no fix for the recklessness in 
> #2.
> 
>> Until the compiler can verify value semantics I am not sure there is a 
>> benefit to `any<value>`.  The semantic distinction is what is important.  
>> There has been discussion about strengthening the “value type == value 
>> semantics” and “reference type == reference semantics” relations but that 
>> hasn’t yet moved beyond talk.
> 
> Don't let the perfect be the enemy of the good. If the algorithm can't 
> possibly work properly with a type that has reference semantics, then 
> rejecting class types is a good first step, even if it doesn't reject every 
> type with reference semantics.

Fair enough.  But in that case I think we want something that does exactly 
that: rejects classes, rather than indicating value semantics.  We need to do 
this in a way that doesn’t lead to a situation where we used the word `value` 
to mean “value type”, and later we have the capability to very value semantics 
and really wish `value` could mean value semantics but that would be a breaking 
change we aren’t willing to make.

> 
>>> (I've read the arguments about pure vs. non-pure value type conformances 
>>> and I'm not convinced. It is always possible to nominally "conform" to a 
>>> protocol in a way that actually undermines its guarantees; for example, you 
>>> could implement `RangeReplaceableCollection.remove(at:)` as a no-op. The 
>>> compiler cannot reject all invalid conformances; it can only reject ones 
>>> which it can trivially show are invalid, because for instance they do not 
>>> even attempt to provide a required method. Similarly, the compiler may not 
>>> be able to prove you are providing value semantics, but it *can* reject 
>>> conformances of reference types to a protocol requiring value semantics, 
>>> since those cannot possibly be valid conformances.
>> 
>> There is a big difference between semantics that the compiler *could* but 
>> *does not yet* verify and semantics that simply cannot be verified.
> 
> And there's also a difference between research projects leading to 
> large-scale changes to foundational language features and incremental fixes. 
> `value` or `!class` or whatever we call it is something we could add to the 
> language without any big redesigns or deep ponderings about the meaning of 
> `==`. It would not be 100%, but it would filter out a fair bit of obviously 
> incorrect code.

I don’t think this is a research project.  IIRC the core team has already 
talked positively about supporting pure functions eventually, for example.  
There is a lot of overlap in the work to verify that a function is pure and the 
work involved in verifying value semantics.

> 
>>> Incidentally, it is not possible to satisfy static property/method 
>>> requirements with cases, but it probably should be:
>> 
>> But that is mostly tangential to this discussion.
> 
> Yes, that is very tangential. Honestly, it's kind of a tangent to a tangent.

I don’t think it’s that far off.  It’s related to Any<enum> because protocols 
with case requirements could only be implemented by an enum.  But that’s a 
discussion for another day (and is probably not the best way to achieve the 
goal behind that request anyway).

> 
> -- 
> Brent Royal-Gordon
> Architechies
> 

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

Reply via email to