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

Matthew Johnson matthew at anandabits.com
Sat May 7 23:11:36 CDT 2016


> On May 7, 2016, at 4:04 PM, Dave Abrahams <dabrahams at apple.com> wrote:
> 
> 
> on Sat May 07 2016, Matthew Johnson <matthew-AT-anandabits.com <http://matthew-at-anandabits.com/>> wrote:
> 
>> I've been thinking about this further and can now state my position more clearly
>> and concisely.
>> 
>> 1. If we're going to have reference types with value semantics the boundary of
>> the value must extend through the reference to the value of the object. Two
>> instances may have the same logical value so reference equality is not good
>> enough.
> 
> My (radical) position has been that we should decree that if you really
> want this thing to have value semantics, it should be a struct.  That
> is, wrap your reference type in a struct and provide an == that looks at
> what's in the instance.  This radically simplifies the model because we
> can then assume that value types have value semantics and reference
> types only have value semantics if you view their identitity as their
> value.

I agree with this longer term, but it is too soon for that.  

Rather than suggest wrapping the reference in a struct I would suggest that most of the time just making it a struct in the first place is the right path.  The problem with this is that it can lead to excessive copying, reference counting, etc if you’re not careful.  I argue that mainstream developers should not need to bother with writing a reference type and wrapping it in a struct just to get around this.  It would be nice if there were better, less boilerplate-y solutions to this in the future.  

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

I have elaborated elsewhere as to why Array<Int> does meet my notion of “pure” value.  I understand that it contains a buffer pointer, etc that does not have value semantics.  But that is an implementation detail and is not externally observable.  I believe that implementation strategies like this are extremely important.  I am only concerned with the externally observable semantics and behavior of the type, not the implementation.  

Just as the internal mutable reference type does not disqualify Array<Int> from having value semantics, it also does not disqualify it from being a “pure value".

> 
>> Purity must include the entire aggregate. Array<UIView> has value
>> semantics but it is not a pure value.
> 
> In what sense does it have value semantics?  Unless we can define
> equality for Array<UIView> it's hard to make any claim about its value
> semantics.

Well it should have value semantics using reference equality of the views because UIView has reference semantics so reference identity is the appropriate definition of equality.  Isn’t that your position as well? 

> 
>> The primary reasons I can think of for creating reference types with value
>> semantics are avoiding copying everything all the time or using inheritance. (I
>> could also list pre-existing types here but am not as concerned with those)
>> 
>> One could argue that you can avoid copying by writing a struct with a handle and
>> one can simulate inheritance by embedding and forwarding. The problem is that
>> this involves a lot of boilerplate and makes your code more complex. 
> 
> The “forwarding boilerplate problem” is something we need to solve in
> the language regardless.  

Yes I agree that it needs to be solved regardless.  In fact, you might remember that I invested quite a bit of effort into drafting a proposal on the topic.  I shelved it mostly because I became very busy with client work, but also partly due to the lukewarm reaction.

> The fact that we don't have an answer today
> shouldn't prevent us from adopting the right model for values and
> references.

I think that depends on what you mean by this.  If you mean providing a default equality of reference identity for reference types I disagree.  I think that should wait until the language reaches a place where there is no good reason to write value semantic reference types.  And I believe the boilerplate currently required to wrap them in a struct is sufficiently burdensome that this is not the case yet.


> 
>> For something like the standard library these concerns are far
>> outweighed by the benefit we all gain by having our collections be
>> value types. However, in application code the benefit may not be worth
>> the cost thus it may be reasonable to prefer immutable objects.
>> 
>> I think there is a viable path for enhancing the language such that there is
>> little or not reason to implement a value semantic type as a reference type. If
>> we were able to declare value types as "indirect" and / or have a compiler
>> supported Box (probably with syntactic sugar) that automatically forwarded
>> calls, performed CoW, etc this would allow us much more control over copying
>> without requiring boilerplate. We could also add something along the lines of
>> Go's embedding (or a more general forwarding mechanism which is my preference)
>> which would likely address many of the reasons for using inheritance in a value
>> semantic reference type.
>> 
>> If we do go down that path I think the case that value semantic types should be
>> implemented as value types, thus reference equality should be the default
>> equality for reference types gets much stronger. In that hypothetical future
>> Swift we might even be able to go so far as saying that reference types with
>> value semantics are an anti-pattern and "outlaw" them. This would allow us to
>> simply say "reference types have reference semantics". 
>> 
>> We might also be able to get to a place where we can "outlaw" value types that
>> do not have value semantics. I haven't thought deeply about that so I'm not
>> certain of the implications, particularly with regards to C interop. IIRC Dave A
>> indicated he would like to see this happen. If this is possible, we may
>> eventually have a language where "value types have value semantics", "some value
>> types are pure values", and "reference types have reference semantics and are
>> never pure values". If it is achievable it would be a significant step forward
>> in simplicity and clarity. 
> 
> So far, I still don't believe that introducing a “pure values” distinction is
> adding simplicity and clarity.  To me it looks like a needless wrinkle.

Fair enough.  I suspect that many folks who have been strongly influenced by functional programming may have a different opinion (btw, I don’t mean to imply anything about the degree to which functional programming has or has not influenced your opinion).

> 
>> Matthew
>> 
>> Sent from my iPad
>> 
>> On May 7, 2016, at 11:17 AM, Matthew Johnson <matthew at anandabits.com> wrote:
>> 
>>    Sent from my iPad
>> 
>>    On May 7, 2016, at 2:21 AM, Andrew Trick via swift-evolution
>>    <swift-evolution at swift.org> wrote:
>> 
>>            On May 6, 2016, at 5:48 PM, Dave Abrahams via swift-evolution
>>            <swift-evolution at swift.org> wrote:
>> 
>>                        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>?
>> 
>>        class Storage {
>>        var element: Int = 0
>>        }
>> 
>>        struct Value {
>>        var storage: Storage
>>        }
>> 
>>        func amIPure(v: Value) -> Int {
>>        v.storage.element = 3
>>        return v.storage.element
>>        }
>> 
>>        I (the optimizer) want to know if 'amIPure' is a pure function. The
>>        developer needs to tell me where the boundaries of the value lie. Does
>>        'storage' lie inside the Value, or outside? If it is inside, then Value
>>        is a 'PureValue' and 'amIPure' is a pure function. To enforce that, the
>>        developer will need to implement CoW, or we need add some language
>>        features.
>> 
>>    Thank you for this clear exposition of how PureValue relates to pure
>>    functions. This is the exact intuition I have about it but you have stated
>>    it much more clearly.
>> 
>>    Language features to help automate CoW would be great. It would eliminate
>>    boilerplate, but more importantly it would likely provide more information
>>    to the compiler.
>> 
>>        If I know about every operation inside 'amIPure', and know where the
>>        value's boundary is, then I don't really need to know that 'Value' is a
>>        'PureValue'. For example, I know that this function is pure without
>>        caring about 'PureValue'.
>> 
>>        func IAmPure(v: Value, s: Storage) -> Int {
>>        var t = v
>>        t.storage = s
>>        return t.storage.element
>>        }
>> 
>>        However, I might only have summary information. I might know that the
>>        function only writes to memory reachable from Value. In that case, it
>>        would be nice to have summary information about the storage type.
>>        'PureValue' is another way of saying that it does not contain references
>>        to objects outside the value's boundary (I would add that it cannot have
>>        a user-defined deinit). The only thing vague about that is that we don't
>>        have a general way for the developer to define the value's boundary. It
>>        certainly should be consistent with '==', but implementing '==' doesn't
>>        tell the optimizer anything.
>> 
>>    I think the ability to define the value's boundary would be wonderful. If we
>>    added a way to do this it would be a requirement of PureValue.
>> 
>>        Anyway, these are only optimizer concerns, and programming model should
>>        take precedence in these discussion. But I thought that might help.
>> 
>>        -Andy
>> 
>>        _______________________________________________
>>        swift-evolution mailing list
>>        swift-evolution at swift.org
>>        https://lists.swift.org/mailman/listinfo/swift-evolution
>> 
> 
> -- 
> -Dave

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160507/07aac27e/attachment.html>


More information about the swift-evolution mailing list