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

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.

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.

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.

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

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

-- 
Brent Royal-Gordon
Architechies

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

Reply via email to