[swift-evolution] Should we rename "class" when referring to protocol conformance?
Matthew Johnson
matthew at anandabits.com
Sat May 7 20:43:05 CDT 2016
> On May 7, 2016, at 3:03 PM, Dave Abrahams <dabrahams at apple.com> wrote:
>
>
> on Sat May 07 2016, Matthew Johnson <matthew-AT-anandabits.com> wrote:
>
>> This depends on the type. For types representing resources, etc it works just
>> fine. But for models it does not work unless the model subgraph is entirely
>> immutable and instances are unique.
>> I agree that it isn't a good idea to provide a default that will
>> certainly be wrong in many cases.
>
> Please show an example of a mutable model where such an equality would
> be wrong.
This is somewhat orthogonal to the main points I have been making in this thread. I have been focused on discussion about reference types that have value semantics and the distinction between value semantics and pure values. In any case, here you go:
let a: NSMutableArray = [1, 2, 3]
let other: NSMutableArray = [1, 2, 3]
let same = a === other // false
let equal = a == other // true
Reference equality does not match the behavior of many existing mutable model types. You seem to be making a case that in Swift it should. But that is a separate discussion from the one I am trying to engage in because mutable reference types *do not* have value semantics.
>
>> I assume what is meant by "PureValue", is any object A, whose own references
>> form a subgraph, within which a change to any of the values would constitute
>> a change in the value of A (thus impermissible if A is immutable). Thus
>> structs would quality as “PureValues”.
>>
>> As you noted in a followup, not all structs qualify. Structs that whose members
>> all qualify will qualify. References to a subgraph that doesn't allow for any
>> observable mutation (i.e. deeply immutable reference types) also qualify.
>>
>> This means the following qualify:
>>
>> * primitive structs and enums
>> * observable immutable object subgraphs
>> * any type composed from the previous
>>
>> It follows that generic types often conditionally qualify depending on their
>> type arguments.
>>
>> I also assume that enforcing immutability on an object graph, via CoW or
>> otherwise, would be unfeasible. You could enforce it on all values
>> accessible by traversing a single reference for reference types, however.
>>
>> This is why I don’t really buy the argument that there is no such this as
>> deep vs shallow copy. Deep copy means copying the whole “PureValue” or
>> subgraph, shallow copy means traversing a single reference and copying all
>> accessible values.
>>
>> I don’t mean to imply that it is the *only* valuable
>> property. However, it I (and many others) do believe it is an
>> extremely
>> valuable
>> property in many cases. Do you disagree?
>>
>> I think I do. What is valuable about such a protocol? What generic
>> algorithms could you write that work on models of PureValue but
>> don't
>> work just as well on Array<Int>?
>>
>> Array<Int> provides the semantics I have in mind just fine so there
>> wouldn’t be
>> any. Array<AnyObject> is a completely different story. With
>> Array<AnyObject> you cannot rely on a guarantee the objects
>> contained
>> in the array will not be mutated by code elsewhere that also happens
>> to have a reference to the same objects.
>>
>> Okay then, what algorithms can you write that operate on PureValue that
>> don't work equally well on Array<AnyObject>?
>
> You haven't answered this question. How would you use this protocol?
I answered elsewhere but I’ll repeat that one use that immediately comes to mind is to constrain values received in the initializer of a (view) controller to ensure that the observable state will not change over time. This is not an algorithmic use but is still perfectly valid IMO.
If I read Andrew’s post correctly it sounds like it may also be of use to the optimizer in some cases.
>
>> let t = MyClass()
>> foo.acceptWrapped(Wrap(t))
>> t.mutate()
>>
>> In this example, foo had better not depend on the wrapped instance
>> not
>> getting
>> mutated.
>>
>> foo has no way to get at the wrapped instance, so it can't depend on
>> anything about it.
>>
>> Ok, but this is a toy example. What is the purpose of Wrap? Maybe
>> foo
>> passes the
>> wrapped instance back to code that *does* have visibility to the
>> instance. My
>> point was that shared mutable state is still possible here.
>>
>> And my point is that Wrap<T> encapsulates a T (almost—I should have
>> let
>> it construct the T in its init rather than accepting a T parameter)
>> and
>> the fact that it's *possible* to code something with the structure
>> of
>> Wrap so that it has shared mutable state is irrelevant.
>>
>> The point I am trying to make is that the semantic properties of
>> Wrap<T> depend
>> on the semantic properties of T (whether or not non-local mutation
>> may be
>> observed in this case).
>>
>> No they do not; Wrap<T> was specifically designed *not* to depend on the
>> semantic properties of T. This was in answer to what you said:
>>
>> A struct wrapping a mutable reference type certainly doesn’t
>> “feel” value semantic to me and certainly doesn’t have the
>> guarantees usually associated with value semantics (won’t
>> mutate behind your back, thread safe, etc).
>>
>> I have been trying to get you to nail down what you mean by PureValue,
>> and I was trying to illustrate that merely being “a struct wrapping a
>> mutable reference type” is not enough to disqualify anything from being
>> in the category you're trying to describe. What are the properties of
>> types in that category, and what generic code would depend on those
>> properties?
>
> Again, the key questions are above, asked a different way.
>
> --
> -Dave
More information about the swift-evolution
mailing list