> On May 6, 2016, at 9:19 PM, Tyler Fleming Cloutier via swift-evolution 
> <[email protected]> wrote:
> 
>> 
>> On May 6, 2016, at 6:54 PM, Dave Abrahams via swift-evolution 
>> <[email protected] <mailto:[email protected]>> wrote:
>> 
>> 
>> on Fri May 06 2016, Matthew Johnson <matthew-AT-anandabits.com 
>> <http://matthew-at-anandabits.com/>> wrote:
>> 
>>>    On May 6, 2016, at 7:48 PM, Dave Abrahams via swift-evolution
>>>    <[email protected] <mailto:[email protected]>> wrote:
>>> 
>>>    on Thu May 05 2016, Matthew Johnson <[email protected] 
>>> <mailto:[email protected]>> wrote:
>>> 
>>>        On May 5, 2016, at 10:02 PM, Dave Abrahams
>>>        <[email protected] <mailto:[email protected]>> wrote:
>>> 
>>>        on Thu May 05 2016, Matthew Johnson <matthew-AT-anandabits.com 
>>> <http://matthew-at-anandabits.com/>> wrote:
>>> 
>>>        On May 5, 2016, at 4:59 PM, Dave Abrahams
>>>        <[email protected] <mailto:[email protected]>> wrote:
>>> 
>>>        on Wed May 04 2016, Matthew Johnson <matthew-AT-anandabits.com 
>>> <http://matthew-at-anandabits.com/>> wrote:
>>> 
>>>        On May 4, 2016, at 5:50 PM, Dave Abrahams via swift-evolution
>>>        <[email protected] <mailto:[email protected]>> wrote:
>>> 
>>>        on Wed May 04 2016, Matthew Johnson
>>>        <[email protected] <mailto:[email protected]>> wrote:
>>> 
>>>        On May 4, 2016, at 1:29 PM, Dave Abrahams via swift-evolution
>>>        <[email protected] <mailto:[email protected]>> wrote:
>>> 
>>>        on Wed May 04 2016, Adrian Zubarev
>>>        <[email protected] <mailto:[email protected]>>
>>>        wrote:
>>> 
>>>        Not sure what to think about the enum cases inside a
>>>        protocol (if AnyEnum would
>>>        even exist), it could be a nice addition to the language, but
>>>        this is an own
>>>        proposal I guess.
>>> 
>>>        We should start by adding AnyValue protocol to which all value
>>>        types
>>>        conforms.
>>> 
>>>        Having a way to constrain conformance to things with value semantics
>>>        is
>>>        something I've long wanted. *However*, the approach described is too
>>>        simplistic. It's possible to build classes whose instances have
>>>        value
>>>        semantics (just make them immutable) and it's possible to build
>>>        structs
>>>        whose instances have reference semantics (just put the struct's
>>>        storage
>>>        in a mutable class instance that it holds as a property, and don't
>>>        do
>>>        copy-on-write). 
>>> 
>>>        In order for something like AnyValue to have meaning, we need to
>>>        impose
>>>        greater order. After thinking through many approaches over the
>>>        years, I
>>>        have arrived at the (admittedly rather drastic) opinion that the
>>>        language should effectively outlaw the creation of structs and enums
>>>        that don't have value semantics. (I have no problem with the idea
>>>        that
>>>        immutable classes that want to act as values should be wrapped in a
>>>        struct). The language could then do lots of things much more
>>>        intelligently, such as correctly generating implementations of
>>>        equality.
>>> 
>>>        That is a drastic solution indeed! How would this impact things like
>>>        Array<UIView>? While Array itself has value semantics, the aggregate
>>>        obviously does not as it contains references which usually be mutated
>>>        underneath us. 
>>> 
>>>        Value semantics and mutation can only be measured with respect to
>>>        equality. The definition of == for all class types would be 
>>> equivalent
>>>        to ===. Problem solved.
>>> 
>>>        Similar considerations apply to simpler wrapper structs such as Weak.
>>> 
>>>        Same answer.
>>> 
>>>        Hmm. If those qualify as “value semantic” then what kind of structs 
>>> and
>>>        enums
>>>        would not? A struct wrapping a mutable reference type certainly 
>>> doesn’t
>>>        “feel”
>>>        value semantic to me and certainly doesn’t have the guarantees 
>>> usually
>>>        associated with value semantics (won’t mutate behind your back, 
>>> thread
>>>        safe,
>>>        etc).
>>> 
>>>        Sure it does.
>>> 
>>>        public struct Wrap<T: AnyObject> : Equatable {
>>>        init(_ x: T) { self.x = x }
>>>        private x: T
>>>        }
>>> 
>>>        func == <T>(lhs: Wrap<T>, rhs: Wrap<T>) -> Bool {
>>>        return lhs.x === rhs.x
>>>        }
>>> 
>>>        I defy you to find any scenario where Wrap<T> doesn't have value
>>>        semantics, whether T is mutable or not.
>>> 
>>>        Alternately, you can look at the Array implementation. Array is a
>>>        struct wrapping a mutable class. It has value semantics by virtue of
>>>        CoW.
>>> 
>>>        This goes back to where you draw the line as to the “boundary of the
>>>        value”.
>>>        Wrap and Array are “value semantic” in a shallow sense and are 
>>> capable
>>>        of deep
>>>        value semantics when T is deeply value semantic. 
>>> 
>>>        No, I'm sorry; this “deep-vs-shallow” thing is a fallacy that comes 
>>> from
>>>        not understanding the boundaries of your value. Or, put more
>>>        solicitously: sure, you can look at the world that way, but it just
>>>        makes everything prohibitively complicated, so why would you want to?
>>> 
>>>        In my world, there's no such thing as a “deep copy” or a “shallow 
>>> copy;”
>>>        there's just “copy,” which logically creates an independent version 
>>> of
>>>        everything up to the boundaries of the value. Likewise, there's no
>>>        “deep value semantics” or “shallow value semantics.” 
>>> 
>>>        Equality defines
>>>        value semantics, and the boundaries of an Array value always includes
>>>        the values of its elements. The *only* problem here is that we have 
>>> no
>>>        way to do equality comparison on some arrays because some types 
>>> aren't
>>>        Equatable. IMO the costs of not having everything be equatable, in
>>>        complexity-of-programming-model terms, are too high.
>>> 
>>>        Thank you for clarifying the terminology for me. This is helpful. 
>>> 
>>>        I think I may have misunderstood what you meant by “boundary of the
>>>        value”. Do
>>>        you mean that the boundary of an Array value stops at the reference
>>>        identity for
>>>        elements with reference semantics? 
>>> 
>>>    Yes.
>>> 
>>>        If you have an Array whose elements are of an immutable reference 
>>> type
>>>        that has value semantics would you say the boundary extends past the
>>>        reference identity of an element and includes a definition of 
>>> equality
>>>        defined by that type?
>>> 
>>>    Yes!
>>> 
>>>        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!!
>>> 
>>>        Both have their place, but the maximum benefit of value semantics
>>>        (purity) 
>>> 
>>>        I don't know what definition of purity you're using. The only one I
>>>        know of applies to functions and implies no side effects. In that
>>>        world, there is no mutation and value semantics is equivalent to
>>>        reference semantics.
>>> 
>>>        I was using it in the sense of “PureValue” as discussed in this
>>>        thread. 
>>> 
>>>    Sorry, this is the first mention I can find in the whole thread, honest.
>>>    Oh, it was a different thread. Joe describes it as a protocol for
>>>    “types that represent fully self-contained values,” which is just fuzzy
>>>    enough that everyone reading it can have his own interpretation of what
>>>    it means.
>>> 
>>>        I was using it to mean values for which no *observable* mutation is
>>>        possible (allowing for CoW, etc). Is there a better term for this 
>>> than
>>>        purity?
>>> 
>>>    You're still not making any sense to me. A type for which no observable
>>>    mutation is possible is **immutable**. The “write” part of
>>>    copy-on-write is a pretty clear indicator that it's all about
>>>    **mutation**. I don't see how they're compatible.
>>> 
>>> Sorry, I did not write that very clearly. I should have said no observable
>>> mutation *that happens behind your back*. In other words, the only 
>>> *observable*
>>> mutation possible is local. 
>> 
>> Yeah, but you need to ask the question, “mutation in what?”  The answer:
>> mutation in the value instance.  Then you need to ask, “how do you
>> determine whether there was mutation?”  
>> 
>>> Immutability accomplishes this by simply prohibiting all
>>> mutation. Primitive value types like Int and structs or enums that
>>> only contain primitive value types accomplish this by getting copied
>>> everywhere.
>>> 
>>> 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. 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".
> 
> 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
> 
>>  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, as shown above. Swift makes it appear as though references and 
> values are on the same level in a way that C does not.
> 
> 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*. 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. 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.
> 
>>>    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”.
> 

Thus structs *without references would qualify.

> I also assume that enforcing immutability on an object graph, via CoW or 
> otherwise, would be unfeasible. 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.
>  
>> 
>>>        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>?
>>> 
>>> Array<Int> provides the semantics I have in mind just fine so there 
>>> wouldn’t be
>>> any.  Array<AnyObject> is a completely different story. With
>>> Array<AnyObject> you cannot rely on a guarantee the objects contained
>>> in the array will not be mutated by code elsewhere that also happens
>>> to have a reference to the same objects.
>> 
>> Okay then, what algorithms can you write that operate on PureValue that
>> don't work equally well on Array<AnyObject>?
>> 
>>> 
>>>        let t = MyClass()
>>>        foo.acceptWrapped(Wrap(t))
>>>        t.mutate()
>>> 
>>>        In this example, foo had better not depend on the wrapped instance 
>>> not
>>>        getting
>>>        mutated.
>>> 
>>>        foo has no way to get at the wrapped instance, so it can't depend on
>>>        anything about it.
>>> 
>>>        Ok, but this is a toy example. What is the purpose of Wrap? Maybe foo
>>>        passes the
>>>        wrapped instance back to code that *does* have visibility to the
>>>        instance. My
>>>        point was that shared mutable state is still possible here. 
>>> 
>>>    And my point is that Wrap<T> encapsulates a T (almost—I should have let
>>>    it construct the T in its init rather than accepting a T parameter) and
>>>    the fact that it's *possible* to code something with the structure of
>>>    Wrap so that it has shared mutable state is irrelevant.
>>> 
>>> The point I am trying to make is that the semantic properties of Wrap<T> 
>>> depend
>>> on the semantic properties of T (whether or not non-local mutation may be
>>> observed in this case). 
>> 
>> No they do not; Wrap<T> was specifically designed *not* to depend on the
>> semantic properties of T.  This was in answer to what you said:
>> 
>>>       A struct wrapping a mutable reference type certainly doesn’t
>>>       “feel” value semantic to me and certainly doesn’t have the
>>>       guarantees usually associated with value semantics (won’t
>>>       mutate behind your back, thread safe, etc).
>> 
>> I have been trying to get you to nail down what you mean by PureValue,
>> and I was trying to illustrate that merely being “a struct wrapping a
>> mutable reference type” is not enough to disqualify anything from being
>> in the category you're trying to describe.  What are the properties of
>> types in that category, and what generic code would depend on those
>> properties?
>> 
>>> It certainly isn’t irrelevant to that point.
>>> 
>>>    HTH,
>>> 
>>>        My expectation is a generic aggregate such as Array would have to
>>>        conditionally conform to AnyValue only when Element also conforms to
>>>        AnyValue.
>>> 
>>>        I’m also wondering how such a rule would be implemented while still
>>>        allowing for CoW structs that *do* implement value semantics, but do
>>>        so while using references internally.
>>> 
>>>        I am not talking about any kind of statically-enforceable rule, 
>>> although
>>>        we could probably make warnings sophisticated enough to help with 
>>> this.
>>> 
>>>        You said the you have arrived at the opinion that the language should
>>>        “effectively outlaw” structs and enums that do not have value 
>>> semantics.
>>>        That
>>>        sounded like static enforcement to me. 
>>> 
>>>        The language outlaws certain kinds of inout aliasing without
>>>        providing static enforcement. This is like that.
>>> 
>>>        I did not know this. Now you have me curious. Can you give an 
>>> example of
>>>        where
>>>        we are able to violate law? I ask mostly because it sounds like 
>>> there is
>>>        a
>>>        possibility of stumbling into dangerous territory, possibly without
>>>        being aware
>>>        that you have done so.
>>> 
>>>        See “In-out Parameters” in
>>>        
>>> https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID362
>>>  
>>> <https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID362>
>>> 
>>>        Maybe you meant we should allow the compiler to assume value 
>>> semantics
>>>        for structs and enums despite the fact that it doesn’t statically
>>>        enforce this?
>>> 
>>>        That would be one *consequence* of effectively outlawing it. The 
>>> library
>>>        could make similar assumptions.
>>> 
>>>        If the compiler can be sophisticated enough to verify value semantics
>>>        statically maybe it would be better to have that mechanism be
>>>        triggered by conformance to AnyValue rather than for all structs and
>>>        enums. Types that conform to AnyValue would receive the benefits of
>>>        the compiler knowing they have value semantics, while other uses of
>>>        structs and enums would remain valid. Best practice would be to
>>>        conform structs and enums to AnyValue whenever possible.
>>> 
>>>        Another possible advantage of this approach would be allowing
>>>        immutable reference types to conform to AnyValue and receive the
>>>        associated benefits such as the generated implementation of equality,
>>>        etc.
>>> 
>>>        -Matthew
>>> 
>>>        -- 
>>>        Dave
>>> 
>>>        _______________________________________________
>>>        swift-evolution mailing list
>>>        [email protected] <mailto:[email protected]>
>>>        https://lists.swift.org/mailman/listinfo/swift-evolution 
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>> 
>>>        _______________________________________________
>>>        swift-evolution mailing list
>>>        [email protected] <mailto:[email protected]>
>>>        https://lists.swift.org/mailman/listinfo/swift-evolution 
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>> 
>>>        -- 
>>>        Dave
>>> 
>>>        _______________________________________________
>>>        swift-evolution mailing list
>>>        [email protected] <mailto:[email protected]>
>>>        https://lists.swift.org/mailman/listinfo/swift-evolution 
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>> 
>>>        -- 
>>>        Dave
>>> 
>>>        -- 
>>>        Dave
>>> 
>>>        _______________________________________________
>>>        swift-evolution mailing list
>>>        [email protected] <mailto:[email protected]>
>>>        https://lists.swift.org/mailman/listinfo/swift-evolution 
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>> 
>>>    -- 
>>>    Dave
>>> 
>>>    _______________________________________________
>>>    swift-evolution mailing list
>>>    [email protected] <mailto:[email protected]>
>>>    https://lists.swift.org/mailman/listinfo/swift-evolution 
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>> 
>> 
>> -- 
>> Dave
>> _______________________________________________
>> swift-evolution mailing list
>> [email protected] <mailto:[email protected]>
>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
> _______________________________________________
> swift-evolution mailing list
> [email protected] <mailto:[email protected]>
> https://lists.swift.org/mailman/listinfo/swift-evolution 
> <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to