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

Tyler Fleming Cloutier cloutiertyler at aol.com
Mon May 16 05:11:36 CDT 2016

> On May 16, 2016, at 2:16 AM, Dave Abrahams <dabrahams at apple.com> wrote:
> on Mon May 16 2016, Tyler Fleming Cloutier <cloutiertyler-AT-aol.com> wrote:
>>>> Super interesting talk! 
>>>> But consider: isn't a single value type able to represent *multiple*
>>>> ethereal types?
>>> “ethereal?”  Does he really use that term?  I don't know what it means.
>> He does pretty frequently. He also refers to them as Mathematical
>> Types. He means the logical type that the C++ implementation is
>> supposed to approximate.
> OK.
>> An example he gives is int and how it only approximates the logical
>> Integer type, because it is represented in many ways, e.g. short, int,
>> long, long long. None of them are exactly the same the logical type.
> Check.
>>>> std::vector is a good example. What are the salient attributes of this
>>>> type? In the talk John says that
>>>> 1. the size is
>>>> 2. the values in the vector are
>>>> 3. the capacity, however *is not*
>>> Yup, just like Array.  Thus the equality test for arrays ignores
>>> capacity.
>>>> in which case std::vector would be an approximation of an ethereal
>>>> type which has a list of values, and the capacity is just an artifact
>>>> of the approximation. But you could also imagine an ethereal type
>>>> which *does* depend of the capacity of the object, and std::vector
>>>> unwittingly approximates that type too! 
>>>> In this case someone, unfamiliar with the implementation might use
>>>> it under the assumption that capacity *is* part of the ethereal type
>>>> and by extension the equality of std::vector.
>>>> John avoids the problem by saying that this must specified in the
>>>> documentation.
>>> Yes.
>>>> I tend to see this as breaking encapsulation since you need to know
>>>> the implementation of the equality operator to be able to determine if
>>>> a public property, the capacity, is part of the ethereal type. 
>>> No, you just need documentation.
>>>> It’s not always the case that you have access to either the
>>>> documentation or the implementation.
>>> Without the documentation, you're lost.  We go a lot further with naming
>>> conventions in Swift than typical C++ does, but even in Swift you can't
>>> expect to fully understand semantics without documentation.
>> Swift also goes a lot further with enforcing things like immutability
>> by default with let, particularly for value types. I think that with
>> well defined rules you can go further with what’s enforced, and remove
>> the need for documentation, especially since the distinctions with
>> regards to value types can be subtle.
> Remove the need for documentation! Good luck with that, my friend.
>>>> This implies, therefore, that if salient attributes *define* the
>>>> immutability of the value type, then the public interface is not
>>>> guaranteed to be immutable, since it is allowed to include non-salient
>>>> attributes. For example, a vector’s capacity could change at any time,
>>>> by virtue of it being stored via a reference.
>>>> What I am saying is that a PureValue is a value type whose public
>>>> interface comprises *only* salient attributes. And I also claim that
>>>> this is a useful distinction amongst value types.
>>> Then Array<Int> is not a PureValue because it exposes capacity?!  That
>>> sounds crazy to me, since the Array's capacity in no sense has reference
>>> semantics.
>> I would argue, rather, that capacity for Array<Int> *is* a salient
>> attribute, by virtue being public 
> Then you're missing the point.  There would be no point in making the
> distinction “salient” if all public API were salient.  Lakos only
> considers public API.

I’m certainly not missing the point. He is saying that not all public API is “salient” because it doesn’t pertain to the logical or ethereal type. I’m saying that not everybody will agree on what the ethereal type is, thus they will not agree on what is salient.

One what to eliminate the possibility of disagreement is do define the ethereal type as what is presented as public API, thus the entire public API is salient. No more need for the “salient” distinction, that all goes away.

Whether or not you think this is a good idea is another thing entirely.

>> and that Array<Int> is a PureValue by virtue of it not having
>> reference semantics. This I propose is valid, because even though it’s
>> not the most intuitive in this case, it is possible that some
>> algorithm interprets capacity to be salient and relies on two arrays
>> with different capacities being not equal.
>> I can certainly understand the opposing argument here, but at least
>> this way it is unambiguous.
> Capacity, in Lakos' terms, is non-salient, and that's unambiguous.

“In Lakos’ terms”

I don’t think many people are familiar with Lakos’ terms, and thus it is quite ambiguous, that’s kinda my point. What is salient to you is not necessarily what is salient to me.

I’m suggesting is that == should have a very well defined, enforceable, and auto-generatable meaning based on the actual bits in a value type and that it should only have this meaning. Other such notions of equality, equivalence, similarity, sameness, or whatever, should be reserved for named functions.

Lakos also talks about this type of thing in the next video.

myRegex.acceptsSameLanguage(as: anotherRegex)

In this way, what is salient is explicit.

==  would just does a component-wise compare on the each of the values, right down to the bits (exactly the same bits that can be rendered immutable with let). With indirect this could be extended to compare the value of arbitrary structures. With references it would just do reference compare. 

struct MyRegex {
	let regexString: String

let x = MyRegex(regexString: “(a+b)* b (a+b)* b (a+b)*")
let y = MyRegex(regexString: “a* b a* b (a+b)*")

y == x // false
y.acceptsSameLanguage(as: x) // true

Obviously there are downsides. People might not expect capacity to be compared. It might cause people to go off and create isEqual functions for everything, if the model of public API == saliency is a poor one. Maybe the solution is to mark things as salient or not salient explicitly.

I will admit that these are orthogonal issues.

Right now public API can be salient and have or be derived from something that has reference semantics. Such types are not PureValues. So I think you are right, I was crazy, so I’ll amend my statement to say Array<Int> would be a PureValue regardless of whether capacity is salient. 

Public API can also be not salient and have value semantics. Such types define an == operator that does not compare all values in the type. Whether or not this is a good or bad thing is independent of PureValues.

>>>> John also says that a salient attribute must derive *only* from the
>>>> state of a particular instance of a type. This by extension implies
>>>> that a salient attribute must derive exclusively from pure
>>>> values. However, this also means that without some “indirect” keyword,
>>>> PureValues are restricted to acyclic and non-recursive structures.
>>>> I also claim that equality can be automatically generated for
>>>> PureValues by equating each of there salient attributes.
>>> That's true for almost any value, provided we define equality for
>>> reference types properly.
>>>> I really apologize if this seems like rambling again, but I am very
>>>> interested in this problem.
>>> I'm glad you are! Few programmers dig far enough to understand value
>>> semantics at a deep level.
>>> All that said, I still think PureValue is a red herring.  Unless I'm
>>> forgetting something that happened in the thread two weeks ago, nobody
>>> has shown me code that relies on PureValue but could not equally well be
>>> written by using a Value constraint.
>> Let me get back to you here. I need to do more investigation!
>> Gotta watch that second video.
> Enjoy!
> -- 
> -Dave

More information about the swift-evolution mailing list