> On May 23, 2016, at 9:22 AM, Dave Abrahams <[email protected]> wrote:
> 
> 
> on Sun May 22 2016, Matthew Johnson <matthew-AT-anandabits.com 
> <http://matthew-at-anandabits.com/>> wrote:
> 
>>> On May 22, 2016, at 3:42 PM, Dave Abrahams via swift-evolution
>>> <[email protected]> wrote:
>>> 
>>> 
>>> on Sun May 22 2016, Matthew Johnson <[email protected]> wrote:
>>> 
>>>> What I am arguing for is the ability to distinguish aggregates which
>>>> are logically isolated from aggregates which contain salient
>>>> references to shared mutable state. 
>>> 
>>> Everything with value semantics is logically isolated in that way.
>> 
>> Array<UIView> has salient references whose referent is shared mutable
>> state.  The references are salient attributes. You’re saying the
>> referent doesn’t matter because the boundary of the value stops at the
>> reference.  
> 
> I'm saying you can define things that way, and it helps to make the
> model for generic programming coherent.  Without something like this, it
> becomes almost impossible to specify the behavior of generic components
> in an understandable way without simultaneously preventing the use of
> reference values as simple object identities, which *are* values.  As I
> mentioned elsewhere, we could force users to wrap these reference values
> to produce something that doesn't have easily identifiable reference
> semantics, but the requirement of your `PureValue` that there should be
> *no way* to derive access to the referenced instance from the wrapped
> reference is too limiting.
> 
>> I’m saying it does matter in that it means the aggregate is no longer
>> logically isolated because shared mutable state is reachable through
>> the aggregate.  Therefore it is not isolated in that way that I was
>> intending to describe.
> 
> This, again, is a matter of your mental model.  I do think it's
> reasonable to say that—especially in Swift where reference-ness is often
> syntactically invisible—asking people to adopt a mental model that
> *directly* treats references as values is simply unrealistic.  In that
> case, using some kind of wrapper to represent object identity might be
> the only practical way to do this.

I think the lack of syntax is a good point.  I have wondered how this would 
play out in Swift.

However, I think it goes deeper than just being syntactically invisible.  If 
Swift used C-like pointer syntax that would give us a syntactic distinction but 
not a semantic one.

Consider your `Set<DrawableObject>` example.  In this case, maybe you really do 
just care about object identity so you can test set membership.  In this case 
you really are just viewing the references as values.  In this case you really 
do view `Set<DrawableObject>` as a pure value.

Consider another example - an `Order` with an `Array<LineItem>`.  In this case, 
you are looking at the array as a part of an aggregate.  You don’t care about 
the values of the references at all, except as a means to get you the object.  
In this case `Array<LineItem>` isn’t a pure value (if `LineItem` is a type with 
reference semantics).

If we simply referred to all reference types with `*DrawableObject` syntax we 
would have to do that in both `Set<*DrawableObject>` and `Array<*LineItem>`.  
The semantic distinction isn’t captured.

However, if we use an opaque wrapper type like you suggest we actually *can* 
capture the distinction.  We can say `Set<Identity<DrawableObject>>`.  It would 
be perfectly acceptable to me to view this as a pure value because it is clear 
that you are only looking at the value of the reference, not actually following 
the reference.  And if we really needed to include “unsafeDereference” or 
something like that for performance reasons I could probably live with that.  
At least your intention is explicit.  If desired we could even introduce 
syntactic sugar for this wrapper like we have for `Optional`, `Array`, and 
`Dictionary` so you can talk about it more concisely.


> 
>>>> To be honest, I am really struggling to understand why this
>>>> distinction seems unimportant to you.
>>> 
>>> The non-exposure of shared mutable state is a hugely important
>>> property of well-encapsulated design.  However, I don't believe it's
>>> appropriate to represent that with a protocol, because I don't
>>> believe there exist any generic components whose correctness depends
>>> on it.
>> 
>> Do you believe it is appropriate to represent this in some other way
>> that allows us to state architectural intent and introduce constraints
>> for the purpose of structuring a large code base?
> 
> Sure, if it makes your life better, you should define it; I just don't
> see yet that it has any place in the standard library.  It is a
> principle of generic programming that protocols (concepts) are
> *discovered* through a specific process.  In the standard library, we
> don't define a new protocol until we have identified a family of
> concrete components that can be generalized based that protocol and
> whose correctness depends on the protocol's constraints.

I hope I am not coming across like I believe we should introduce this right 
now, or like I am attached to the specific mechanism of a PureValue protocol.  
I am trying to advocate that there should be a long term goal in Swift that 
allows us to talk about types from which you cannot reach shared mutable state, 
hopefully have these types verified by the compiler, and use this as a generic 
constraint.  I could easily see the mechanism being at the language level: 
`pure struct`, `pure enum` and a `pure` protocol constraint similar to the 
`class` protocol constraint we have today (and of course `pure func` as well).  
I know this is not going to happen in Swift 3 and probably not Swift 4, but I 
do think it is a very worthwhile goal.

Going on a tangent a bit.  I agree with the general goal of keeping the 
standard library very focused.  On the other hand, I do think this criteria for 
protocols is a bit to restrictive.  How do you define the criteria for whether 
a type gets into the standard library or not?  Why not just use that same 
criteria for protocols?  If it would be very useful to the Swift community to 
share a common protocol that is always available why would it be excluded just 
because no component of the standard library depends on that protocol for its 
correctness? 

A good example of this is the `Map` protocol that came up recently.  It is 
reasonably common to have code which only depends on the ability to read and / 
or write to a key value data structure and doesn’t need to be concerned with 
the underlying implementation.  It can be very useful to use a `Map` 
abstraction to hide the implementation.  The standard library includes types 
that could conform to this protocol.  It seems to me like a good idea to 
include a protocol like this in the standard library even if the implementation 
of its components does not depend on this protocol for correctness.

> 
> HTH,
> 
> -- 
> -Dave

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to