> 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
