[swift-evolution] Should we rename "class" when referring to protocol conformance?
Dave Abrahams
dabrahams at apple.com
Sun May 22 13:10:21 CDT 2016
on Mon May 16 2016, Matthew Johnson <swift-evolution at swift.org> wrote:
>> On May 16, 2016, at 1:39 AM, Dave Abrahams <dabrahams at apple.com> wrote:
>>
>>
>> on Sun May 15 2016, Tyler Fleming Cloutier <cloutiertyler-AT-aol.com <http://cloutiertyler-at-aol.com/>> wrote:
>>
>
>>> On May 15, 2016, at 11:48 AM, Dave Abrahams via swift-evolution <swift-evolution at swift.org> wrote:
>>>
>>> on Mon May 09 2016, Matthew Johnson <matthew-AT-anandabits.com> wrote:
>>>
>>> On May 8, 2016, at 1:51 AM, Dave Abrahams <dabrahams at apple.com> wrote:
>>>
>>> on Sat May 07 2016, Andrew Trick <atrick-AT-apple.com> wrote:
>>>
>>> On May 7, 2016, at 2:04 PM, Dave Abrahams <dabrahams at apple.com> wrote:
>>>
>>> 2. Value types are not "pure" values if any part of the aggregate
>>> contains a
>>> reference whose type does not have value semantics.
>>>
>>> Then Array<Int> is not a “pure” value (the buffer contained in an
>>> Array<Int> is a mutable reference type that on its own, definitely does
>>> *not* have value semantics). I don't think this is what you intend, and
>>> it indicates that you need to keep working on your definition.
>>>
>>> It sounds like you’re changing the definition of value semantics to make it
>>> impossible to define PureValue.
>>>
>>> Not on purpose.
>>>
>>> Does Array<T> have value semantics then only if T also has value
>>> semantics?
>>>
>>> This is a great question; I had to rewrite my response four times.
>>>
>>> In my world, an Array<T> always has value semantics if you respect the
>>> boundaries of element values as defined by ==. That means that if T is
>>> a mutable reference type, you're not looking through references, because
>>> == is equivalent to ===.
>>>
>>> Therefore, for almost any interesting SomeConstraint that doesn't refine
>>> ValueSemantics, then
>>>
>>> Array<T: SomeConstraint>
>>>
>>> only has value semantics if T has value semantics, since SomeConstraint
>>> presumably uses aspects of T other than reference identity.
>>>
>>> The claim has been made that Array always has value semantics,
>>> implying that the array value’s boundary ends at the boundary of it’s
>>> element values.
>>>
>>> Yes, an array value ends at the boundary of its elements' values.
>>>
>>> That fact is what allows the compiler to ignore mutation of the
>>> buffer.
>>>
>>> I don't know what you mean here.
>>>
>>> It's perfectly clear that Array<T> is a PureValue iff T is a PureValue.
>>> PureValue is nothing more than transitive value semantics.
>>>
>>> You're almost there. “Transitive” implies that you are going to look at
>>> the parts of a type to see if they are also PureValue's. So which parts
>>> of the Array struct does one look at, and why? Just tell me the
>>> procedure for determining whether a type is a PureValue.
>>>
>>> We look at the observable parts.
>>>
>>> That begs the question. The “parts” of an Array are the observable
>>> features that are considered by equality.
>>>
>>> We do not look at unobservable parts because we want flexibility to
>>> use things like CoW, shared immutable references, etc in our
>>> implementation.
>>>
>>> IMO the important thing when it comes to functional purity is not what
>>> you *can* observe, but what you *do* observe.
>>>
>>> Can you share your definition of value semantics?
>>>
>>> Explaining it well and in sufficient detail for this discussion takes
>>> some doing, but I think John Lakos and I share an understanding of value
>>> semantics and he has a really detailed explanation in
>>> https://www.youtube.com/watch?v=W3xI1HJUy7Q and
>>> https://www.youtube.com/watch?v=0EvSxHxFknM. He uses C++ in places,
>>> but it's not particularly advanced, and the fundamental ideas apply just
>>> as well to Swift.
>>>
>>> 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.
>>
>>>
>>> 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.
>>
>>> 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.
>>
>>> 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.
>
> Looking forward to hearing your thoughts on the code I just posted.
>
> In addition to that, I think it is also important to note that
> contained within the notion of PureValue is thread safety. For
> example, in CSP PureValues would can be sent as messages on a channel.
> Clearly sending Array<MutableReferenceType> over a channel is
> something you would not do in CSP.
Not if you were interested in anything other than the identities of the
elements. But I can easily imagine representing a selection as a Set<T>
and passing that across threads, where T might be a mutable reference
type (using my default definition of == and hashValue for all
references).
> You can also freely share a ImmutableBox<MyPureValue> and read from it
> in any thread you wish without any trouble. You cannot do that with
> ImmutableBox<Array<MutableReferenceType>>. In the latter, you cannot
> mutate the array, but you can still mutate the objects it contains
> references to.
Again, it depends on what you look at. We could create a value type
that distinguishes object identities, e.g.
struct Identity<SomeClass : AnyObject> : Hashable, Comparable {
let subject: SomeClass
init(subject: SomeClass) { self.subject = subject }
...
}
func == <X>(lhs: Identity<X>, rhs: Identity<X>) -> Bool {
return lhs === rhs
}
then you could write:
ImmutableBox<Array<Identity<MutableReferenceType>>>
But would you really make it *impossible* to observe the subject of an
Identity? We could, but I'm not sure I would. It would come at a cost
to efficiency in those places where you have a collection of Identity's
and you happen to know they're not shared across threads, which I think
is probably an important use-case.
>>
>>> Tyler
>>>
>>> It may be helpful
>>> if we start there and refine your definition to exclude impure value
>>> types like Array<UIView>.
>>>
>>> In the meantime I’ll take another shot:
>>>
>>> 1. Scalars are pure values.
>>>
>>> 2. Any aggregate type with value semantics is a pure value iff all
>>> observable parts of the aggregate are pure values.
>>>
>>> --
>>> -Dave
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>
>> --
>> -Dave
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
--
-Dave
More information about the swift-evolution
mailing list