[swift-evolution] Should we rename "class" when referring to protocol conformance?

Dave Abrahams dabrahams at apple.com
Sat May 7 14:52:55 CDT 2016

on Fri May 06 2016, Tyler Fleming Cloutier <cloutiertyler-AT-aol.com> wrote:

>     On May 6, 2016, at 6:54 PM, Dave Abrahams via swift-evolution
>     <swift-evolution at swift.org> wrote:
>     on Fri May 06 2016, Matthew Johnson <matthew-AT-anandabits.com> wrote:
>         On May 6, 2016, at 7:48 PM, Dave Abrahams via swift-evolution
>         <swift-evolution at swift.org> wrote:
>         Swift’s collections also accomplish this through copying, but only when
>         the
>         elements they contain also have the same property. 
>     Only if you think mutable class instances are part of the value of the
>     array that stores references to those class instances. As I said
>     earlier, you can *take* that point of view, but why would you want to?
>     Today, we have left that question wide open, which makes the whole
>     notion of what is a logical value very indistinct. I am proposing to
>     close it.
>         On the other hand, it is immediately obvious that non-local mutation
>         is quite possibly in the elements of a Swift Array<AnyObject> unless
>         they are all uniquely referenced.
>     If you interpret the elements of the array as being *references* to
>     objects, there is no possibility of non-local mutation. If you
>     interpret the elements as being objects *themselves*, then you've got
>     problems.
> This does not make sense, because you’ve got problems either way. You are
> arguing, essentially, that everything is a value type because
> references/pointers are a value. 

I am arguing that every type can be viewed as a value, allowing us to
preserve a sense in which Array<T> has value semantics irrespective of
the details of T.

> If that were the case then the *only* valid way to compare the
> equality of types would be to compare their values. Overriding the
> equality operator would inherently violate the property of
> immutability, i.e. two immutable objects can change their equality
> even without mutation of their “values".

Not at all.  In my world, you can override equality such that it
includes referenced storage when either:

1. the referenced storage will not be mutated
2. or, the referenced storage will only mutated when uniquely-referenced.

> func ==(lhs, rhs) {
> ...
> }
> class MyClass {
> var a: Int
> ...
> } 
> let x = MyClass(a: 5)
> let y = MyClass(a: 5)
> x == y // true
> y.a = 6
> x == y // false

I don't understand what point you're trying to make, here.  I see that x
and y are immutable. Notwithstanding the fact that the language tries to
hide the difference between a reference and the instance to which it
refers from the user (the difference would be clearer if you had to
write y->a = 6 as in C, but it's still there), that immutability doesn't
extend beyond the variable binding.  The class instance to which y
refers, as you've ably demonstrated, is mutable.

>     Are you arguing that reference types should be equatable by default,
>     using
>     equality of the reference if the type does not provide a custom
>     definition of
>     equality?
>     Yes!!
> Custom definitions of equality, inherently, decouple immutability from
> equality,

Not a bit.  They certainly *can* do that, if we allow it, but I am
proposing to ban that.  There are still useful custom definitions of
equality as I have outlined above.

> as shown above. Swift makes it appear as though references and values
> are on the same level in a way that C does not.

Yep.  It's an illusion that breaks down at the edges and can be really
problematic if users fully embrace it.  You can't write a generic
algorithm with well-defined semantics that does mutation-by-part on
instances of T without constraining T to have value or reference semantics.

I am not advocating that we require “y->a” for class member access, but
I *am* suggesting that we should accept the fact that reference and
value semantics are fundamentally different and make design choices
(including language rules) accordingly.

> let x = MyStruct()
> let y = MyClass()
> x.myFoo
> y.myFoo
> vs
> my_struct *x = …
> my_struct y = …
> x.my_foo
> y->my_foo
> With C it is explicit that you are crossing a reference. Thus there is only
> *one* type of equality in C, that the values *are equal*. 

Well, C doesn't even *have* struct equality;

> This exactly the type of equality you are referring to, but this does
> not carry over to Swift for precisely the reason that Swift paves over
> the difference between value and reference types, and then allows you
> to redefine equality.
> Therefore, in essentially no circumstances does it make sense to
> compare a type by its reference if it has any associated data in
> Swift. 

Disagreed.  Instances whose *identity* is significant, i.e. basically
everything that actually ought to be a class, can be very usefully
compared by their references.  For example, if equality and hashing were
defined for UIViews, based on their references, you could use a
Set<UIView> to keep track of which views had user interaction during a
given time interval.

> Basically, if it will be commonplace to override the equality operator
> to compare the first level of associated values of a reference type,
> then the comparison of just the reference has no business being the
> default.
> If the default equality for reference types was defined as the equality of the
> references it would be inconsistent with the Swift’s current apparent surfacing
> of the first level of associated data for reference types.

Yes, but as I've said, that illusion doesn't work in the presence of

>         I think perhaps what you mean by “purity” is just, “has value
>         semantics.” But I could be wrong.
>         No, an array storing instances of reference types that are not immutable
>         would
>         not be “pure” (or whatever you want to call it).
>         is derived from deep value semantics. This is when there is no
>         possibility of shared mutable state. This is an extremely important
>         property.
>         It's the wrong property, IMO.
>         Wrong in what sense? 
>         Wrong in the sense that it rules out using things like Array that are
>         logically value types but happen to be implemented with CoW, and if you
>         have proper encapsulation there's no way for these types to behave as
>         anything other than values, so it would be extremely limiting. 
>         I’m a big fan of CoW as an implementation detail. We have definitely
>         been
>         miscommunicating if you thought I was suggesting something that would
>         prohibit
>         CoW.
>     Then what, precisely, are the syntactic and semantic requirements of
>     “PureValue?”
> 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”.

OK, one vote for that interpretation noted.

> I also assume that enforcing immutability on an object graph, via CoW
> or otherwise, would be unfeasible. 

I presume by “enforcing” you mean, “enforcing by the compiler.”  It's
very easy to enforce that for particular object graphs in library code,
using encapsulation.

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

Well, again, “you can look at the world that way, but why would you want
to?”  It makes reasoning about code exponentially more difficult if at
every level you have to ask whether a copy is deep or shallow.


More information about the swift-evolution mailing list