Yes, my personal view is that the result you describe would be a cogent one. It would not be wrong to name everything the same way but I do feel that Span’s API is just fine for itself and suitable for other types with similar semantics.
Beyond whether or not choosing one name does not make things worse… do we both agree that there is some value in choosing one name across all these concrete types for the purposes of improving consistency and clarity?
Is your POV that isIdentical(to:) is the superior name for Span and that there is no value in choosing one name across all types?
Or is your POV that isIdentical(to:) is the superior name for Spanin spite of the value in choosing one name across all types?
Let's maybe try this from another direction… here is the feedback from LSG that suggested we choose isTriviallyIdentical(to:):
Let's maybe try and focus on point number one: "triviality".
The isTriviallyIdentical(to:) method on Stringwill not be memcmp. This was discussed in one of our pitch threads:
If the feedback from LSG is we want to ship isTriviallyIdentical(to:) on String… then the feedback from LSG is that all that code I linked to falls under the "umbrella of triviality".
So let's look at Span. Here is the actual shipping implementation of isIdentical(to:) on Span:
That's it. One line of code performing an O(1) operation.
So again… all that code and all that conditional logic from String I linked to we are prepared to ship under the umbrella of triviality. But the feedback now is that one line of code — literally one line of code — from Span is somehow… not trivial? How can this be possible?
not meant to be a normative requirement, and without meaning to open discussion about padding ↩︎
We are discussing behavior and semantics, not how implemented. The point being that there are numerous possible notions of identicality for String, and this function looks to a kind of “trivial identicality,” or it looks to some sort of identicality in a “trivial way” (i.e., not too deeply), or both—and in so doing it declines to bless a single canonical notion of identicality without qualifier. Span already has only one reasonable, well-defined notion of identicality and its isIdentical(to:) captures that fully; it is not necessary to qualify it because it admits no reasonable confusion with another notion of identicality.
Span represents a memory region. If two spans are identical, they represents the same memory region (per documentation). If we’re using it, we’re most likely trying to see if an operation resulted in a specific range of elements. The result will be deterministic if the two spans come from the same source.
String and Array are collections. The memory region used to store the elements is mostly abstracted away as an implementation detail. The result here may depend on what the compiler/optimizer decided to do with ARC and literals. If we check whether they are identical, we’re most likely trying to create a fast path in code skipping some work when the opportunity arises. It does not necessarily have anything to do with memory regions as the small-string representation shows. So I think it’s semantically a different operation that deserves a different name. I’m starting to think it should not have “identical” in the name because then it becomes confusing with Span.isIdentical which is about comparing memory regions.
So this is me basically rejecting of isTriviallyIdentical (or any “identical” spelling) in favor of something else. I think my preference would go with the name isSubstitutable, answering the question about whether swapping two values will have any effect.
This is also a good name for deriving a Substitutable protocol later (same word).
I suppose we could also call it isTriviallySubstitutable if we want to emphasize the O(1) part (the hypothetical protocol perhaps becoming TriviallySubstitutable).
Ok. Fair enough. You want to discuss behavior and you want to discuss semantics and you do not want to discuss implementation. Got it.
Just to be clear here… are these statements your personal opinion or are these statements the consensus of the LSG at this time?
After 168 public comments and many attempts on your part to teach me what Span.isIdentical(to:) really means I believe at this point you should please just tell me — without referencing implementation — what Span.isIdentical(to:) really means.
Again — going back to what you said earlier — please tell me what Span.isIdentical(to:) really means in terms of behavior and semantics without discussing any implementation details of Span.
I mean… I have a hard time believing there is no reasonable confusion here. After 168 public comments… clearly there exists some confusion and some lack of consensus about what Span.isIdentical(to:) really means.
Here is another LSG engineer sharing their personal opinion:
So it seems to me like there does not exist a strong consensus here. Someone here clearly seems to be reasonably confused about the true nature of Span.isIdentical(to:) and how this does — or does not — relate to String.
Xiaodi is just speaking for himself. It should generally be assumed that evolution workgroup members are only speaking for themselves when they post. The review manager of the current review is the only person who's really meant to be speaking for the workgroup in a review thread, and they're supposed to always make it crystal clear what's coming from them and what's coming from the broader workgroup. I certainly hope that's true of my posts here, like in the post above about the Span methods: the first paragraph is direction from the LSG, and the second paragraph is my own opinion about what should be done.
I think I've already communicated what the LSG does and does not have consensus about right now, but if anyone has questions about it, requests for clarification are always welcome.
John can certainly speak for himself, but I don’t see anything to suggest that he is confused about what Span.isIdentical(to:) does.
Two spans are identical when they represent the same contiguous region of memory. A contiguous region of memory is identified by its address and element count.
Absolutely, we’re speaking for ourselves unless we say explicitly otherwise. Although to give proper credit, those statements are an echo of John’s personal opinion above:
"Contiguous region of memory"? "Address and element count"? That all sounds an awful lot like implementation details to me.
So the message here — and please correct me if I'm wrong about this — seems to be that the feedback I left previously, which leveraged the implementation details of String to support my opinion that Span.isIdentical(to:) performs "more trivial" work than String.isTriviallyIdentical(to:), is somehow not impactful feedback because we want to only discuss behavior and semantics. But the feedback you left, which does leverage the implementation details of Span to support your opinion, is impactful feedback? Because now all of a sudden we really do want to define behavior and semantics with implementation details?
How can one engineer not be allowed to leverage implementation details to support their opinion but a different engineer can be allowed to leverage implementation details to support their opinion?
I still have not seen any persuasive argument here that the nature of Span.isIdentical(to:) can be defined — through behavior and semantics and without implementation details — with any kind of meaningful or important difference from what we propose here for String. And I certainly don't see a big enough difference between these two to support us shipping two different names in this one proposal.
If you can't define the nature of Span.isIdentical(to:) without implementation details… fair enough. It's only fair then that my feedback which leveraged the implementation details of String is totally fair and legit. Which implies that Span is performing "more trivial" work than what takes place inside String. And if we ship String.isTriviallyIdentical(to:) there's no reason for us to imply that the work being performed on Span is in any way "not trivial" or "less trivial".
I think it is worth noting that this is only the case because we have not, yet, adopted a generalization of Collection for ~Copyable element types. Once we have such a generalization I think it'd be reasonable to consider same-elements another potentially confusable form of identicalness which it would be reasonable to use "trivial" to distance ourselves from.
I take @xwu's point to be that for this family of types, the fact that they model a contiguous region of memory is indeed more than an implementation detail—it is precisely what these types are for, and what they are documented as modeling:
Span<Element> represents a contiguous region of memory which contains initialized instances of Element.
However, I don't broadly buy that, because Span et al more saliently expose a singular notion of identity than do String et al, the former family has only a single notion of identicalness—"identity" is not merely the noun form of "identical". Colloquially one can talk about "identical twins" where the notion of identicalness is invoked at a different level than mere identity (DNA-level). One can also talk about whether two copies of the same book are identical, where the notion of identical is probably something akin to "produced according to the same printing plan", or perhaps even "as part of the same produciton run".
Which I suppose is all to say that I'm on board with the idea that if we adopt isTriviallyIdentical(to:) we ought to be consistent about it. I don't think the differences for the Span family of types here, which I agree exist, are so salient as to deserve a totally different name.
To clarify my personal position, while I see your point that perhaps Span has a more intrinsic concept of identicality than these other types, I don't actually think that's an important distinction to make in the names, and I think there's a real consistency benefit to just using the same name for all of these methods that essentially do the same thing. I think we should rename the existing methods to isTriviallyIdentical(to:).
isTriviallyIdentical(to:) makes sense to me. I don't think we should be in a rush to rename the Span methods, but consistency in API design is a good thing and Span is still quite new, so the damage/churn from renaming them should be manageable.
Span is meaningfully different from, say Array, in that it "what it is" is precisely a region of memory, rather than an abstracted collection value. Two Spans are identical if they represent the same region of memory.
Array is more subtle: an Array is an abstract value, not a region of memory. The comparison that the proposed operation does is about implementation details of Array, and mostly divorced from their actual values. So it's not at all unreasonable to suggest that these two things could have distinct names. But it also doesn't mean that they have to. In one case the operation corresponds to the fundamental nature of the type, and in the other it corresponds to an implementation detail, but at the point of use, anywhere you would use isTriviallyEqual(to:) on Array, you would also use it on Span, so giving them the same name makes good sense.
This to me is an argument for adding === to the interface of Span specifically, because a span has an identity as strong as a reference-counted object. (But that's beyond the scope of this proposal, so I'll pipe down.)