[Draft] Rename Sequence.elementsEqual

Because, in my analysis, the problem is that the method is incorrectly named. The problem affects all types that conform to Sequence and not just Set and Dictionary; elementsEqual is a distinct function from ==, and it must either continue to be distinct or cease to exist, but its name does nothing to clarify any distinction.

In my analysis, the problem is the method's implementation. As I see it, the only use for `elementsEqual` is as a replacement for `==` when two objects are different types (or not known to be the same)—equal elements, and IF the sequences have an order, in the same order. Could you provide an example where `elementsEqual` randomly returning either true or false depending on internal state alone is a legitimate and desirable result?

It doesn’t randomly return true or false, it consistently returns true or false for the *same* pair of Sequences. What *same* means, of course, is complicated and exists at two levels (as we have two ways of talking about *same*).

I didn't mean literally random, but the result for any two equal sets is unpredictable and depends on implementation details.

I apologize for not reading every email in depth in this thread (they are coming in faster than I can parse them), but let me try to present motivation for this and hopefully provide more shared understanding.

We have two forms of equality we’re talking about: equality of Sequence and equality of the elements of Sequences in their respective ordering. `==` covers the former, and I’ll use the existing (harmful) name of `elementsEqual` for the latter.

`==` conveys substitutability of the two Sequences. This does not necessarily entail anything about their elements, how those elements are ordered, etc., it just means two Sequences are substitutable. `elementsEqual` means that the two Sequences produce substitutable elements. These are different concepts and both are independently useful.

Cases:

1. Two Sequences are substitutable and produce substitutable elements when iterated. `==` and `elementsEqual` both return true.

Example: Two arrays with the same elements in the same order.

2. Two Sequences are substitutable, but do not produce substitutable elements when iterated. `==` returns true, while `elementsEqual` returns false.

Example: Two Sets that contain the same elements but in a different order.

Contrived Example: Two Lorem Ipsum generators are the same generator (referentially equal, substitutable for the purposes of my library), but they sample the user’s current battery level (global state) each time they produce text to decide how fancy to make the faux Latin. They’re substitutable, but don’t generate the same sequence.

Evidently I disagree with your definition of "substitutable." How can you say one thing can be substituted for another when doing so gives a different result?

3. Two Sequences are not substitutable, but produce substitutable elements when iterated. `==` returns false, while `elementsEqual` returns true.

Example: Consider two sequences that have differing identity. `==` operates on an identity level, `elementsEqual` operates at an element level.

Contrived Example: InfiniteMonkeys and Shakespeare both produce the same sonnet, but they’re not substitutable for my library’s purposes.

The way I see it, this is exactly the reason for this function—determining whether two objects give the same sequence without regards to their types.

4. Two Sequences are not substitutable and don’t produce substitutable elements when iterated. `==` and `elementsEqual` both return false.

Example: `[1,2,3]` compared to `[4,5,6]`

It is true that situations #2 and #3 are a little harder to grok, but they are what illustrate the subtle difference at hand. I think situation #2 is the most confusing, and has been the primary focus of this thread as Set exists and exhibits it.

Indeed. My opinion is that #2 is not just confusing, but an entirely invalid result/state.

···

On Oct 17, 2017, at 11:47 AM, Michael Ilseman <milseman@apple.com> wrote:

On Oct 17, 2017, at 10:15 AM, Kevin Nattinger via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Now, onto naming. `elementsEqual` is a very poor choice of name for the concept of equality of elements in their respective orderings, as it doesn’t highlight the “in their respective orderings” part. `lexicographicallyEqual` highlights the ordering much better, as “abc” is not lexicographically equal to “cba” despite having equal elements. I think it is clearly an improvement over the status quo. I like something a little more explicit (e.g. `elementsOrderedEqual`), personally, but I don’t care that strongly. I’m just glad to see `elementsEqual` getting some clarification.

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Hi Gwendal, these are use cases for using indices as unordered references which certainly are useful. I was looking for use cases depending on the order exposed by Sequence, specifically use cases for elementsEqual.

-Thorsten

···

Am 17.10.2017 um 09:03 schrieb Gwendal Roué <gwendal.roue@gmail.com>:

Modeling is, by definition, imperfect. The question is, what imperfect model is most useful _to Swift_. The idea is that conforming Set and Dictionary to Collection is on balance more useful than not doing so; that having two protocols, Sequence and Collection, is on balance more useful than having one or three, and that the set of semantic guarantees of Collection are on balance more useful than other possible sets of semantic guarantees.

That is your idea which is disputed and underlined with arguments whereas you keep repeating that Set behaves as dictated by its conformance without giving use cases why this should be useful.

Hello,

You can't *control* the ordering of a set or a dictionary, but you can still *rely* on it.

For example, to find a key in a dictionary that is associated a given value, you can rely on the fact that a dictionary's order is guaranteed to be stable, and that on top of that its indexes can address the dictionary itself, but also its keys and values sequences. The code below has no bug;

let dict = ["a": "foo", "b": "bar", "c": "needle"]

// Find a key associated with "needle"
if let index = dict.values.index(of: "needle") {
    let key = dict.keys[index]
    print(key) // prints "c"
}

It's more difficult to find a use case for set's ordering and indexes. But since you ask, here is an example. The goal is to find any element which is not equal to another value, in any collection:

extension Collection where Element: Equatable {
    /// Returns any element which is not equal to the given element
    func anyElement(notEqualTo v: Element) -> Element? {
        if let i = index(of: v) {
            if let alt = index(i, offsetBy: 1, limitedBy: endIndex), alt != endIndex {
                return self[alt]
            }
            if i == startIndex {
                return nil
            }
            return first
        }
        return first
    }
}

Set([1, 2, 3]).anyElement(notEqualTo: 1) // 2 or 3
Set([1, 2]).anyElement(notEqualTo: 1) // 2
Set([1]).anyElement(notEqualTo: 1) // nil
Set([2]).anyElement(notEqualTo: 1) // 2
Set().anyElement(notEqualTo: 1) // nil

That *can* be useful, isn't it?

Gwendal Roué

Thanks for the post, that's the first clear explanation i see on this
thread for the concepts behind the design for Sequence.

I am a bit afraid that understanding all that is a bit above what to expect
the average swift developer will guess when he sees functions like "prefix
/ first / elementEqual (or whatever it's called)" on the Set type.
There is, IMHO, a much higher chance he'll either :
1/ not understand anything, or
2/ think Sets are in fact secretely ordered sets, or start writing generic
extensions above Sequence or Collection thinking those protocols are
synonymous for orderer collections.

1/ is pretty harmless, but 2/ seems like a source of bug.

My personal opinion after reading all this is that we should simply change
the name to sequentiallyEquals (just because it let people hint at where
this function comes from, and not let them believe that it's something
native to the Set type), and not touch anything else, until maybe we
someday have a way to make everything perfect for everyone.

···

On Tue, Oct 17, 2017 at 8:47 PM, Michael Ilseman via swift-evolution < swift-evolution@swift.org> wrote:

On Oct 17, 2017, at 10:15 AM, Kevin Nattinger via swift-evolution < > swift-evolution@swift.org> wrote:

Because, in my analysis, the problem is that the method is incorrectly
named. The problem affects all types that conform to Sequence and not just
Set and Dictionary; elementsEqual is a distinct function from ==, and it
must either continue to be distinct or cease to exist, but its name does
nothing to clarify any distinction.

In my analysis, the problem is the method's implementation. As I see it,
the only use for `elementsEqual` is as a replacement for `==` when two
objects are different types (or not known to be the same)—equal elements,
and IF the sequences have an order, in the same order. Could you provide an
example where `elementsEqual` randomly returning either true or false
depending on internal state alone is a legitimate and desirable result?

It doesn’t randomly return true or false, it consistently returns true or
false for the *same* pair of Sequences. What *same* means, of course, is
complicated and exists at two levels (as we have two ways of talking about
*same*).

I apologize for not reading every email in depth in this thread (they are
coming in faster than I can parse them), but let me try to present
motivation for this and hopefully provide more shared understanding.

We have two forms of equality we’re talking about: equality of Sequence
and equality of the elements of Sequences in their respective ordering.
`==` covers the former, and I’ll use the existing (harmful) name of
`elementsEqual` for the latter.

`==` conveys substitutability of the two Sequences. This does not
necessarily entail anything about their elements, how those elements are
ordered, etc., it just means two Sequences are substitutable.
`elementsEqual` means that the two Sequences produce substitutable
elements. These are different concepts and both are independently useful.

Cases:

1. Two Sequences are substitutable and produce substitutable elements when
iterated. `==` and `elementsEqual` both return true.

Example: Two arrays with the same elements in the same order.

2. Two Sequences are substitutable, but do not produce substitutable
elements when iterated. `==` returns true, while `elementsEqual` returns
false.

Example: Two Sets that contain the same elements but in a different order.

Contrived Example: Two Lorem Ipsum generators are the same generator
(referentially equal, substitutable for the purposes of my library), but
they sample the user’s current battery level (global state) each time they
produce text to decide how fancy to make the faux Latin. They’re
substitutable, but don’t generate the same sequence.

3. Two Sequences are not substitutable, but produce substitutable elements
when iterated. `==` returns false, while `elementsEqual` returns true.

Example: Consider two sequences that have differing identity. `==`
operates on an identity level, `elementsEqual` operates at an element level.

Contrived Example: InfiniteMonkeys and Shakespeare both produce the same
sonnet, but they’re not substitutable for my library’s purposes.

4. Two Sequences are not substitutable and don’t produce substitutable
elements when iterated. `==` and `elementsEqual` both return false.

Example: `[1,2,3]` compared to `[4,5,6]`

It is true that situations #2 and #3 are a little harder to grok, but they
are what illustrate the subtle difference at hand. I think situation #2 is
the most confusing, and has been the primary focus of this thread as Set
exists and exhibits it.

Now, onto naming. `elementsEqual` is a very poor choice of name for the
concept of equality of elements in their respective orderings, as it
doesn’t highlight the “in their respective orderings” part.
`lexicographicallyEqual` highlights the ordering much better, as “abc” is
not lexicographically equal to “cba” despite having equal elements. I think
it is clearly an improvement over the status quo. I like something a little
more explicit (e.g. `elementsOrderedEqual`), personally, but I don’t care
that strongly. I’m just glad to see `elementsEqual` getting some
clarification.

Thanks,

Jon

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Really? I would have thought an elementwise comparison is exactly what
[Float].== would do.
I tested this and these are the (very surprising, at least to me)
results:
let numbers: [Float] = [1, .nan]
let copy = numbers
let sameNumbers: [Float] = [1, .nan]

numbers == copy // (1) true (!)
numbers == sameNumbers // (2) false (!)
numbers.elementsEqual(copy) // (3) false (ok)
numbers.elementsEqual(sameNumbers) // (4) false (ok)

So [Float].== returns a different result depending on whether you
compare two arrays that share the same buffer (1) or two arrays with the
same contents (2) when the array contains NaN. The reason is that
Array.== immediately returns true if two arrays share the same
buffer[1].
Even knowing that comparing floats is problematic in the context of NaN
(and in general), I find this very surprising. It also seems to clash
with Michael's idea[2] that two substitutable sequences should return
true for ==.

Links:

  1. https://github.com/apple/swift/blob/master/stdlib/public/core/Arrays.swift.gyb#L2194-L2197
  2. [swift-evolution] [Draft] Rename Sequence.elementsEqual

···

On Tue, Oct 17, 2017, at 20:46, Xiaodi Wu via swift-evolution wrote:

On Tue, Oct 17, 2017 at 12:54 Jonathan Hull <jhull@gbis.com> wrote:

Why was elementsEqual created? Isn’t it meant to check equality of
two sequences in a generic context where == isn’t available?>

No no no no no no no no. That’s precisely why the name is misleading.
elementsEqual is *not* simply a mixed-type version of ==. Remember
that == as implemented on a concrete sequence type *has no obligation*
to use elementwise comparison using the element’s implementation of
==. This is not merely theoretical: [Float].== does *not* do an
elementwise comparison using Float.==. By contrast, you are guaranteed
a true elementwise comparison with elementsEqual regardless of how
equivalence is defined for the sequence.

Because, in my analysis, the problem is that the method is incorrectly named. The problem affects all types that conform to Sequence and not just Set and Dictionary; elementsEqual is a distinct function from ==, and it must either continue to be distinct or cease to exist, but its name does nothing to clarify any distinction.

In my analysis, the problem is the method's implementation. As I see it, the only use for `elementsEqual` is as a replacement for `==` when two objects are different types (or not known to be the same)—equal elements, and IF the sequences have an order, in the same order. Could you provide an example where `elementsEqual` randomly returning either true or false depending on internal state alone is a legitimate and desirable result?

It doesn’t randomly return true or false, it consistently returns true or false for the *same* pair of Sequences. What *same* means, of course, is complicated and exists at two levels (as we have two ways of talking about *same*).

It is random (for unordered collections) from the standpoint of business logic. The order of the elements of a Set, while publicly observable, cannot be influenced by business logic because it is an implementation detail. Therefore it is not possible to write business logic that exploits this order, i.e. it is not possible to write business logic using `elementsEqual` on a Set.

The only exception is using the receiver as argument, i.e. `set.elementsEqual(set)` because in this case, and in this case alone, the result is independent of the effectively random order.
Therefore the single use case for `elementsEqual` for unordered collections is calling `set.elementsEqual(set)` to determine whether a Set of Floats contains NaN values. I claim that this is better written as `set.contains { $0.isNaN }` which works nicely for a Set<Float> and is intention revealing as opposed to using `elementsEqual`.

As I have shown this really is the *single* use case for `elementsEqual` for unordered collections. There can be no other use case.
All other uses of `elementsEqual` involving two different collections where one is an unordered collection have random results that have absolutely no business meaning and therefore are bugs.

Therefore having `elementsEqual` as API for unordered collections is a source of bugs except for a single use case which has a better solution using `contains`. That is why I would like to split the API.

Adam expressed the concern that this required `map` to implemented separately for the split APIs: `Iterable.map` would have to return an `Iterable` whereas `Sequence.map` would have to return a `Sequence`.
This is an independent issue IMO, because currently we have a similar situation: `Collection.map` does not return a `Collection` either. It returns an Array. This is a compromise partly due to missing language features which can simply remain when `Sequence` is split.

Actually the question for the correct return type for `map` is not a simple one. For a `Dictionary` we would probably want to get a `Dicitonary` where the values have been replaced by the mapped ones (this is how Smalltalk does it). But for `Set` we typically do *not* want a `Set` because in general we will be interested in duplicate mapped values. There are times where a `Set` would be the correct return type but this applies to ordered collections or dictionaries as well, so there should be a variant (or an argument with a default) allowing to provide the desired result type or target collection.
But this is a completely separate issue which can be easily deferred until the planned language features have arrived which might help finding a good solution for this question. Until then we will just use the current implementation which returns an array.

To get back to the topic at hand: I propose to differentiate between unordered and ordered collections because I think that this is an important distinction with tractable impact on algorithms (as shown above). The method `elementsEqual` would be part of the ordered sequence protocol and I would suggest the name `elementsPairwiseEqual`.

-Thorsten

···

Am 17.10.2017 um 20:47 schrieb Michael Ilseman via swift-evolution <swift-evolution@swift.org>:

On Oct 17, 2017, at 10:15 AM, Kevin Nattinger via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I apologize for not reading every email in depth in this thread (they are coming in faster than I can parse them), but let me try to present motivation for this and hopefully provide more shared understanding.

We have two forms of equality we’re talking about: equality of Sequence and equality of the elements of Sequences in their respective ordering. `==` covers the former, and I’ll use the existing (harmful) name of `elementsEqual` for the latter.

`==` conveys substitutability of the two Sequences. This does not necessarily entail anything about their elements, how those elements are ordered, etc., it just means two Sequences are substitutable. `elementsEqual` means that the two Sequences produce substitutable elements. These are different concepts and both are independently useful.

Cases:

1. Two Sequences are substitutable and produce substitutable elements when iterated. `==` and `elementsEqual` both return true.

Example: Two arrays with the same elements in the same order.

2. Two Sequences are substitutable, but do not produce substitutable elements when iterated. `==` returns true, while `elementsEqual` returns false.

Example: Two Sets that contain the same elements but in a different order.

Contrived Example: Two Lorem Ipsum generators are the same generator (referentially equal, substitutable for the purposes of my library), but they sample the user’s current battery level (global state) each time they produce text to decide how fancy to make the faux Latin. They’re substitutable, but don’t generate the same sequence.

3. Two Sequences are not substitutable, but produce substitutable elements when iterated. `==` returns false, while `elementsEqual` returns true.

Example: Consider two sequences that have differing identity. `==` operates on an identity level, `elementsEqual` operates at an element level.

Contrived Example: InfiniteMonkeys and Shakespeare both produce the same sonnet, but they’re not substitutable for my library’s purposes.

4. Two Sequences are not substitutable and don’t produce substitutable elements when iterated. `==` and `elementsEqual` both return false.

Example: `[1,2,3]` compared to `[4,5,6]`

It is true that situations #2 and #3 are a little harder to grok, but they are what illustrate the subtle difference at hand. I think situation #2 is the most confusing, and has been the primary focus of this thread as Set exists and exhibits it.

Now, onto naming. `elementsEqual` is a very poor choice of name for the concept of equality of elements in their respective orderings, as it doesn’t highlight the “in their respective orderings” part. `lexicographicallyEqual` highlights the ordering much better, as “abc” is not lexicographically equal to “cba” despite having equal elements. I think it is clearly an improvement over the status quo. I like something a little more explicit (e.g. `elementsOrderedEqual`), personally, but I don’t care that strongly. I’m just glad to see `elementsEqual` getting some clarification.

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Modeling is, by definition, imperfect. The question is, what imperfect model is most useful _to Swift_. The idea is that conforming Set and Dictionary to Collection is on balance more useful than not doing so; that having two protocols, Sequence and Collection, is on balance more useful than having one or three, and that the set of semantic guarantees of Collection are on balance more useful than other possible sets of semantic guarantees.

That is your idea which is disputed and underlined with arguments whereas you keep repeating that Set behaves as dictated by its conformance without giving use cases why this should be useful.

Hello,

You can't *control* the ordering of a set or a dictionary, but you can still *rely* on it.

For example, to find a key in a dictionary that is associated a given value, you can rely on the fact that a dictionary's order is guaranteed to be stable, and that on top of that its indexes can address the dictionary itself, but also its keys and values sequences. The code below has no bug;

let dict = ["a": "foo", "b": "bar", "c": "needle"]

// Find a key associated with "needle"
if let index = dict.values.index(of: "needle") {
    let key = dict.keys[index]
    print(key) // prints "c"
}

You are using the index from one collection to index into another collection. Isn’t that something the documentation explicitly tells us not to do? Or is there a special (documented) guarantee that these collections will always sync indices?

The documentation of Dictionary.values states that

"When iterated over, values appear in this collection in the same order as they occur in the dictionary’s key-value pairs."

and similarly for Dictionary.keys. So that should be safe, as I understand it.

Martin

···

On 17. Oct 2017, at 09:48, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

On Oct 17, 2017, at 12:04 AM, Gwendal Roué via swift-evolution <swift-evolution@swift.org> wrote:

I guess you could do:

  if let key = dict.first(where {$1 == “needle}).0 {
    print(key)
  }

…although note that which element is “first” if there are multiple keys with needle as the value may shift around on you as you mess with the dictionary (and also on separate runs even if you don’t mess with the dictionary).

It's more difficult to find a use case for set's ordering and indexes. But since you ask, here is an example. The goal is to find any element which is not equal to another value, in any collection:

extension Collection where Element: Equatable {
    /// Returns any element which is not equal to the given element
    func anyElement(notEqualTo v: Element) -> Element? {
        if let i = index(of: v) {
            if let alt = index(i, offsetBy: 1, limitedBy: endIndex), alt != endIndex {
                return self[alt]
            }
            if i == startIndex {
                return nil
            }
            return first
        }
        return first
    }
}

Set([1, 2, 3]).anyElement(notEqualTo: 1) // 2 or 3
Set([1, 2]).anyElement(notEqualTo: 1) // 2
Set([1]).anyElement(notEqualTo: 1) // nil
Set([2]).anyElement(notEqualTo: 1) // 2
Set().anyElement(notEqualTo: 1) // nil

That *can* be useful, isn't it?

Yes, although this could easily be provided using iteration without indexing.

Note that my current favorite solution is to simply provide an additional guarantee on set/dictionary that the iteration order will always be the same for the same contents (regardless of history), but the order would still be otherwise arbitrary. That is a non-source-breaking change (renaming IS source breaking). It won’t fix everything, but it will fix some of the biggest gotchas. As a bonus, elementsEqual will 'just work'™ for sets/dictionaries.

Thanks,
Jon

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Thanks for the post, that's the first clear explanation i see on this thread for the concepts behind the design for Sequence.

I am a bit afraid that understanding all that is a bit above what to expect the average swift developer will guess when he sees functions like "prefix / first / elementEqual (or whatever it's called)" on the Set type.
There is, IMHO, a much higher chance he'll either :
1/ not understand anything, or
2/ think Sets are in fact secretely ordered sets, or start writing generic extensions above Sequence or Collection thinking those protocols are synonymous for orderer collections.

1/ is pretty harmless, but 2/ seems like a source of bug.

My personal opinion after reading all this is that we should simply change the name

Exactly, and that’s what Xiaodi’s proposal does. Confronted with these complexities, his proposal reasons that a name change is the lessor or evils, as in the “Proposed solution” section.

to sequentiallyEquals

Xiaodi floated “lexicographicallyEqual” which is accurate and a big improvement, and I’m liking it more and more. I floated “sequentiallyEquals”, which I’m liking less and less now. My current preference is for “elementsOrderedEqual” which I think is more descriptive but unwieldy. On the other hand, this is a far less common facility, and order matters, so maybe more verbose names are fine.

As you can tell, I’m pretty wishy-washy with names and probably should abstain from bike shedding anything important ;-)

(just because it let people hint at where this function comes from, and not let them believe that it's something native to the Set type), and not touch anything else, until maybe we someday have a way to make everything perfect for everyone.

We can ease the pain a bit. E.g. warnings/deprecation when the type is concretely known to be Set, etc. Not a general solution, but might fight some bugs.

I think that of all the dichotomies we fail to model in our protocol hierarchy, the ordered vs unordered is the least useful to accommodate. It is, however, easy and fun to bike shed.

···

On Oct 17, 2017, at 12:54 PM, Benjamin G <benjamin.garrigues@gmail.com> wrote:

On Tue, Oct 17, 2017 at 8:47 PM, Michael Ilseman via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Oct 17, 2017, at 10:15 AM, Kevin Nattinger via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Because, in my analysis, the problem is that the method is incorrectly named. The problem affects all types that conform to Sequence and not just Set and Dictionary; elementsEqual is a distinct function from ==, and it must either continue to be distinct or cease to exist, but its name does nothing to clarify any distinction.

In my analysis, the problem is the method's implementation. As I see it, the only use for `elementsEqual` is as a replacement for `==` when two objects are different types (or not known to be the same)—equal elements, and IF the sequences have an order, in the same order. Could you provide an example where `elementsEqual` randomly returning either true or false depending on internal state alone is a legitimate and desirable result?

It doesn’t randomly return true or false, it consistently returns true or false for the *same* pair of Sequences. What *same* means, of course, is complicated and exists at two levels (as we have two ways of talking about *same*).

I apologize for not reading every email in depth in this thread (they are coming in faster than I can parse them), but let me try to present motivation for this and hopefully provide more shared understanding.

We have two forms of equality we’re talking about: equality of Sequence and equality of the elements of Sequences in their respective ordering. `==` covers the former, and I’ll use the existing (harmful) name of `elementsEqual` for the latter.

`==` conveys substitutability of the two Sequences. This does not necessarily entail anything about their elements, how those elements are ordered, etc., it just means two Sequences are substitutable. `elementsEqual` means that the two Sequences produce substitutable elements. These are different concepts and both are independently useful.

Cases:

1. Two Sequences are substitutable and produce substitutable elements when iterated. `==` and `elementsEqual` both return true.

Example: Two arrays with the same elements in the same order.

2. Two Sequences are substitutable, but do not produce substitutable elements when iterated. `==` returns true, while `elementsEqual` returns false.

Example: Two Sets that contain the same elements but in a different order.

Contrived Example: Two Lorem Ipsum generators are the same generator (referentially equal, substitutable for the purposes of my library), but they sample the user’s current battery level (global state) each time they produce text to decide how fancy to make the faux Latin. They’re substitutable, but don’t generate the same sequence.

3. Two Sequences are not substitutable, but produce substitutable elements when iterated. `==` returns false, while `elementsEqual` returns true.

Example: Consider two sequences that have differing identity. `==` operates on an identity level, `elementsEqual` operates at an element level.

Contrived Example: InfiniteMonkeys and Shakespeare both produce the same sonnet, but they’re not substitutable for my library’s purposes.

4. Two Sequences are not substitutable and don’t produce substitutable elements when iterated. `==` and `elementsEqual` both return false.

Example: `[1,2,3]` compared to `[4,5,6]`

It is true that situations #2 and #3 are a little harder to grok, but they are what illustrate the subtle difference at hand. I think situation #2 is the most confusing, and has been the primary focus of this thread as Set exists and exhibits it.

Now, onto naming. `elementsEqual` is a very poor choice of name for the concept of equality of elements in their respective orderings, as it doesn’t highlight the “in their respective orderings” part. `lexicographicallyEqual` highlights the ordering much better, as “abc” is not lexicographically equal to “cba” despite having equal elements. I think it is clearly an improvement over the status quo. I like something a little more explicit (e.g. `elementsOrderedEqual`), personally, but I don’t care that strongly. I’m just glad to see `elementsEqual` getting some clarification.

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Because, in my analysis, the problem is that the method is incorrectly named. The problem affects all types that conform to Sequence and not just Set and Dictionary; elementsEqual is a distinct function from ==, and it must either continue to be distinct or cease to exist, but its name does nothing to clarify any distinction.

In my analysis, the problem is the method's implementation. As I see it, the only use for `elementsEqual` is as a replacement for `==` when two objects are different types (or not known to be the same)—equal elements, and IF the sequences have an order, in the same order. Could you provide an example where `elementsEqual` randomly returning either true or false depending on internal state alone is a legitimate and desirable result?

It doesn’t randomly return true or false, it consistently returns true or false for the *same* pair of Sequences. What *same* means, of course, is complicated and exists at two levels (as we have two ways of talking about *same*).

I didn't mean literally random, but the result for any two equal sets is unpredictable and depends on implementation details.

Exactly, and that’s why having this method is important. If you wish to pass a Set to a generic operation that cares about the order (e.g. generic over Sequence and not e.g. SetAlgebra), it is only substitutable with another Sequence if it happens to have the same ordering of the same elements. All orderings for Sets are arbitrary, at some level.

I apologize for not reading every email in depth in this thread (they are coming in faster than I can parse them), but let me try to present motivation for this and hopefully provide more shared understanding.

We have two forms of equality we’re talking about: equality of Sequence and equality of the elements of Sequences in their respective ordering. `==` covers the former, and I’ll use the existing (harmful) name of `elementsEqual` for the latter.

`==` conveys substitutability of the two Sequences. This does not necessarily entail anything about their elements, how those elements are ordered, etc., it just means two Sequences are substitutable. `elementsEqual` means that the two Sequences produce substitutable elements. These are different concepts and both are independently useful.

Cases:

1. Two Sequences are substitutable and produce substitutable elements when iterated. `==` and `elementsEqual` both return true.

Example: Two arrays with the same elements in the same order.

2. Two Sequences are substitutable, but do not produce substitutable elements when iterated. `==` returns true, while `elementsEqual` returns false.

Example: Two Sets that contain the same elements but in a different order.

Contrived Example: Two Lorem Ipsum generators are the same generator (referentially equal, substitutable for the purposes of my library), but they sample the user’s current battery level (global state) each time they produce text to decide how fancy to make the faux Latin. They’re substitutable, but don’t generate the same sequence.

Evidently I disagree with your definition of "substitutable." How can you say one thing can be substituted for another when doing so gives a different result?

They are substitutable for the purposes of certain operations. The key question is “what” gives a different result.

Sets are primarily about membership, and equal sets are substitutable (hand wavy) for Set-like purposes. But, two equal Sets are not substitutable for iteration purposes unless they produce the same elements in the same order. This latter requirement is far less important than the former for Sets, but can still come up in generic contexts.

Two Lorem Ipsum generators could be equal in that they are substitutable for (hand wavy) faux-Latin generation purposes, even if the sequence of generated characters happens to differ. But, if I cared about the sequences of generated characters being the same, then they are only substitutable if they produce the same characters in the same order. For example, this has ramifications on how I might test my generators.

I agree that this case is a little unintuitive, and I’m still nailing it down in my mind.

3. Two Sequences are not substitutable, but produce substitutable elements when iterated. `==` returns false, while `elementsEqual` returns true.

Example: Consider two sequences that have differing identity. `==` operates on an identity level, `elementsEqual` operates at an element level.

Contrived Example: InfiniteMonkeys and Shakespeare both produce the same sonnet, but they’re not substitutable for my library’s purposes.

The way I see it, this is exactly the reason for this function—determining whether two objects give the same sequence without regards to their types.

What if they are the same type? What if they are the same type but that type has identity? These are two different and (potentially) orthogonal axes.

4. Two Sequences are not substitutable and don’t produce substitutable elements when iterated. `==` and `elementsEqual` both return false.

Example: `[1,2,3]` compared to `[4,5,6]`

It is true that situations #2 and #3 are a little harder to grok, but they are what illustrate the subtle difference at hand. I think situation #2 is the most confusing, and has been the primary focus of this thread as Set exists and exhibits it.

Indeed. My opinion is that #2 is not just confusing, but an entirely invalid result/state.

It isn’t *entirely* invalid if even contrived examples exist. It might be harmful for some types such as Set, and an imperfect remedy might be to warn when used on concrete Set types. Certainly not calling it `elementsEqual` but instead something that implies there’s ordering impact is an improvement, however incremental.

···

On Oct 17, 2017, at 2:08 PM, Kevin Nattinger <swift@nattinger.net> wrote:

On Oct 17, 2017, at 11:47 AM, Michael Ilseman <milseman@apple.com <mailto:milseman@apple.com>> wrote:

On Oct 17, 2017, at 10:15 AM, Kevin Nattinger via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Now, onto naming. `elementsEqual` is a very poor choice of name for the concept of equality of elements in their respective orderings, as it doesn’t highlight the “in their respective orderings” part. `lexicographicallyEqual` highlights the ordering much better, as “abc” is not lexicographically equal to “cba” despite having equal elements. I think it is clearly an improvement over the status quo. I like something a little more explicit (e.g. `elementsOrderedEqual`), personally, but I don’t care that strongly. I’m just glad to see `elementsEqual` getting some clarification.

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

I read along with most of the thread and like to add one thing. Maybe it’s
not the right way to look at things, but I see protocol extensions as being
part of any type that conforms to the protocol. As it is now, code
completion on a Set will present me with the elementsEqual function, which
in my view is misleading, since it cannot be used in any useful way with a
Set.

By renaming elementsEqual we might be able to make it less misleading, but
it will always be a useless function that is part of the Set namespace (or
Dictionary for that matter).

I don’t know what the best solution would be, but to me it feels like
distinguishing between Iterable and Sequence, or Ordered and Unordered
makes sense, and it might be good to at least investigate what the impact
would be.

Regards,
/Manolo

···

On Tue, 17 Oct 2017 at 21:00, Jonathan Hull via swift-evolution < swift-evolution@swift.org> wrote:

On Oct 17, 2017, at 11:47 AM, Michael Ilseman via swift-evolution < > swift-evolution@swift.org> wrote:

`==` conveys substitutability of the two Sequences. This does not
necessarily entail anything about their elements, how those elements are
ordered, etc., it just means two Sequences are substitutable.
`elementsEqual` means that the two Sequences produce substitutable
elements. These are different concepts and both are independently useful.

I agree that ‘==‘ conveys substitutability. Here is the issue:

let a = Set([1,2,3,4,5])
let b = Set([5,4,3,2,1])

a == b //True, they are substitutable

[1,2,3,4,5].elementsEqual(a) //True
[1,2,3,4,5].elementsEqual(b) //False… I guess they weren’t actually
substitutable after all

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

That ordering can be arbitrary, but it shouldn’t leak internal

representation such that the method used to create identical things affects
the outcome of generic methods because of differences in internal
representation.

It would be better to say that the iteration order is
well-defined. That will almost always mean documented, and usually
predictable though obviously e.g. RNGs and iterating in random order will
not be predictable by design.

That's actually more semantically constrained than what Swift
calls a `Collection` (which requires conforming types to be multi-pass
and(?) finite). By contrast, Swift's `SpongeBob` protocol explicitly
permits conforming single-pass, infinite, and/or unordered types.

I think you’re talking about Sequence here, I’ve lost track of
your nonsense by now. Yes, the current Swift protocol named Sequence allows
unordered types. You seem to keep asserting that but not actually
addressing my argument, which is *that allowing Sequences to be
unordered with the current API is undesired and actively harmful, and
should* *therefore** be changed*.

What is harmful about it?

After thinking about it, I think the harmful bit is that
unordered sequences are leaking internal representation (In your example,
this is causing people to be surprised when two sets with identical
elements are generating different sequences/orderings based on how they
were created). You are correct when you say that this problem is even true
for for-in.

I would not say it is a problem. Rather, by definition, iteration
involves retrieving one element after another; if you're allowed to do that
with Set, then the elements of a Set are observably ordered in some way.
Since it's not an OrderedSet--i.e., order doesn't matter--then the only
sensible conclusion is that the order of elements obtained in a for...in
loop must be arbitrary. If you think this is harmful, then you must believe
that one should be prohibited from iterating over an instance of Set.
Otherwise, Set is inescapably a Sequence by the Swift definition of
Sequence. All extension methods on Sequence like drop(while:) are really
just conveniences for common things that you can do with iterated access;
to my mind, they're essentially just alternative ways of spelling various
for...in loops.

I think an argument could be made that you shouldn’t be able to
iterate over a set without first defining an ordering on it (even if that
ordering is somewhat arbitrary). Maybe we have something like a
“Sequenc(e)able” protocol which defines things which can be turned into a
sequence when combined with some sort of ordering. One possible ordering
could be the internal representation (At least in that case we are calling
it out specifically). If I had to say
“setA.arbitraryOrder.elementsEqual(setB.arbitraryOrder)” I would definitely
be less surprised when it returns false even though setA == setB.

Well, that's a totally different direction, then; you're arguing
that `Set` and `Dictionary` should not conform to `Sequence` altogether.
That's fine (it's also a direction that some of us explored off-list a
while ago), but at this point in Swift's evolution, realistically, it's not
within the realm of possible changes.

I am actually suggesting something slightly different. Basically,
Set and Dictionary’s conformance to Collection would have a different
implementation. They would conform to another protocol declaring that they
are unordered. That protocol would fill in part of the conformance to
sequence/collection using a default ordering, which is mostly arbitrary,
but guaranteed to produce the same ordering for the same list of elements
(even across collection types). This would be safer, but a tiny bit slower
than what we have now (We could also potentially develop a way for
collections like set to amortize the cost). For those who need to recover
speed, the new protocol would also define a property which quickly returns
a sequence/iterator using the internal ordering (I arbitrarily called it
.arbitraryOrder).

I believe it would not be source breaking.

That is indeed something slightly different.

In an ideal world--and my initial understanding of what you were
suggesting--Set and Dictionary would each have a member like `collection`,
which would expose the underlying data as a `SetCollection` or
`DictionaryCollection` that in turn would conform to `Collection`;
meanwhile, Set and Dictionary themselves would not offer methods such as
`prefix`, or indexing by subscript, which are not compatible with being
unordered. For those who want a particular ordering, there'd be something
like `collection(ordered areInIncreasingOrder: (T, T) -> Bool) ->
{Set|Dictionary}Collection`.

What you suggest here instead would be minimally source-breaking.
However, I'm unsure of where these guarantees provide benefit to justify
the performance cost. Certainly not for `first` or `dropFirst(_:)`, which
still yields an arbitrary result which doesn't make sense for something
_unordered_. We *could* have an underscored customization point named
something like `_customOrderingPass` that is only invoked from
`elementsEqual` or other such methods to pre-rearrange the internal
ordering of unordered collections in some deterministic way before
comparison. Is that what you have in mind?

Something like that. Whatever we do, there will be a tradeoff
between speed, correctness, and ergonomics.

My suggestion trades speed for correctness, and provides a way to
recover speed through additional typing (which is slightly less ergonomic).

You haven't convinced me that this is at all improved in
"correctness." It trades one arbitrary iteration order for another on a
type that tries to model an unordered collection.

We could do something like you suggest. I don’t think the method
would need to be underscored… the ordering pass could just be a method on
the protocol which defines it as unordered. Then we could provide a
special conformance for things where order really matters based on
adherence to that protocol. That might be an acceptable tradeoff. It
would give us speed at the cost of having the correct implementation being
less ergonomic and more error prone (you have to remember to check that it
is unordered and call the ordering method when it mattered).

I’d still be a bit worried that people would make incorrect generic
algorithms based on expecting an order from unordered things, but at least
it would be possible for them check and handle it correctly. I think I
could get behind that tradeoff/compromise, given where we are in the swift
process and Swift's obsession with speed (though I still slightly prefer
the safer default). At least the standard library would handle all the
things correctly, and that is what will affect the majority of programmers.

What is an example of such an "incorrect" generic algorithm that
would be made correct by such a scheme?

To start with, the one you gave as an example at the beginning of
this discussion: Two sets with identical elements which have different
internal storage and thus give different orderings as sequences. You
yourself have argued that the confusion around this is enough of a problem
that we need to make a source-breaking change (renaming it) to warn people
that the results of the ‘elementsEqual’ algorithm are undefined for sets
and dictionaries.

No, I am arguing that the confusion about ‘elementsEqual’ is foremost
a problem with its name; the result of this operation is not at all
undefined for two sets but actually clearly defined: it returns true if two
sets have the same elements in the same iteration order, which is a
publicly observable behavior of sets (likewise dictionaries).

But it is a behavior which has absolutely no meaning at all because
the order does not depend on the elements of the set but on the history of
how the set has been reached its current state.
So why should I ever use this method on a set?
What is the use case?

One example: you can use it to check an instance of Set<Float> to
determine if it has a NaN value. (The “obvious” way of doing it is not
guaranteed to work since NaN != NaN.)

How would I do that? I'd rather expect to use a property isNaN on Float
to do that.

set.elementsEqual(set)

I see why that would work (thanks to Set being a collection vs a
sequence), but it still feels like a hack. I definitely wouldn’t want to
be maintaining code with that in it. Especially when compared to something
like:

set.contains(where: {$0.isNaN})

The purpose of protocols is to enable useful generic code. You cannot use
isNaN for code that works on generic Collection, or for that matter, even
code that works on Set where Element : Numeric.

Much generic code (for example, generic sorting) easily blows up when
encountering NaN. If you want an algorithm that is robust enough to handle
(or at least reject) that scenario, then you will need to check for it. It
is not a “hack” but a very common and accepted way of determining the
presence of NaN.

Just because a hack is commonly accepted or common doesn’t mean it isn’t
a hack.

It’s not a “hack.” NaN is required by IEEE fiat not to be equivalent to
itself. Asking whether a value is reflexively equivalent to itself is
guaranteed to be sufficient to detect the presence of NaN in all
IEEE-compliant contexts, which is to say all past, present, and future
versions of Swift.

The problem isn’t Nan != NaN, but the fact that you are comparing T != T
(where T may not even be a floating point thing) and assuming it means NaN.

I’m not assuming that at all; the example I gave was the use case of
generic sorting, where NaN is problematic *because* NaN != NaN. In a
generic context, I would want to detect the case where *any value*, NaN or
not, is not equivalent to itself. NaN is just a good example, because it is
a value of a built-in type that exhibits this behavior.

Using elementsEqual in this way (in a generic context which may or may not

involve floats) is definitely a hack, and is likely to bite you at some
point.

How is it definitely a hack when it is blessed by IEEE, and how can it
bite me?

Because it may return false for reasons other than NaN.

Which, again, is fine. If I cared about NaN specifically, then I must write
a generic algorithm that’s constrained to floating point.

I don’t see why a non-source-breaking change is suddenly off-limits.

But more than that, any generic algorithm which is assuming that the
sequence is coming from an ordered source (i.e. many things using
first/last). Some uses of first are ok because the programmer actually
means ‘any’, but anywhere where they actually mean first/last may be
problematic.

Such as...?

Currently, there is no way to test for ordered-ness, so there is no

way for even a careful programmer to mitigate this problem. By adding a
protocol which states that something is unordered, we can either branch on
it, or create a separate version of an algorithm for things which conform.

It is clearly the case that Swift’s protocol hierarchy fits sets and
collections imperfectly; however, it is in the nature of modeling that
imperfections are present. The question is not whether it is possible to
incur performance, API surface area, and other trade-offs to make the model
more faithful, but rather whether this usefully solves any problem. What is
the problem being mitigated? As I write above, Swift’s Set and Dictionary
types meet the semantic requirements for Collection and moonlight as
ordered collections. What is a generic algorithm on an ordered collection
that is “not OK” for Set and Dictionary? (“elementsEqual”, as I’ve said,
is not such an example.)

On the contrary, `elementsEqual` is exactly such an example, because
it makes no sense to use it on a Set.

let s1 = Set([1,2,3,4,5,6])
let s2 = Set([6,5,4,3,2,1])

Both sets have different iteration orders. Comparing those sets with
some other collection using `elementsEqual` will give no meaningful result
because the order - and therefore the result of `elementsEqual` - is in
effect random.

No, it is not such an example; it’s misleadingly named but works
correctly—that is, its behavior matches exactly the documented behavior,
which relies on only the semantic guarantees of Sequence, which Set
correctly fulfills.

Fulfills to the letter. Again, what can you do with it if the result is
random??

The result is not random.

It is undefined though. As you said earlier, by the guarantees we have
been given, it may shift over different builds/runs of a program. Thus in
one run, it might return true and then false in another (without changing
our code). As Greg pointed out, it is also possible with the guarantees we
are given, for set/dict to have different orderings with copies of
themselves. (It will happen for sure when deep copying a dictionary with
reference-type keys).

As I wrote to Greg, if that is a possibility for Set, then the
implementation is problematic for conformance to Collection and needs to be
re-examined.

So why not fix both things at once with a single change?

I’m not sure why you claim the order of a dictionary will definitiely
change on deep copying. But in any case, we are not talking about deep
copying here, or at least, I’m not; we’re talking about the notional
copying involved in the semantics of passing a set as an argument.

Because the ordering of some dictionaries depends on the memory address
of the key. There is no way to copy the key in those cases without
changing the ordering. You can watch their ordering shift in the debugger
from run to run.

I’m fine with that. Instances of reference types have identity. A deep
copy of a dictionary *has different keys* from the original. This may not
be so from the perspective of a custom == operator, but it most certainly
is so from the perspective of reference type semantics. So to me it is fine
(correct, even?) that a deep copy of a dictionary has a different iteration
order—it has different keys.

Again, this is something entirely different from notional copies made in
the course of passing values as arguments.

As I keep saying, relying on the behavior of a leaking internal

implementation is a bad plan.

Again, it is not a leaking internal implementation. It is a public API
guarantee.

The public API guarantee is that there will be some ordering that is
stable for a particular instance on a particular run (as long as it is not
mutated/copied). The actual ordering is undefined.

Any generic code which depends on a particular ordering will have output
which is undefined.

Generic code is incorrect that “depends on a particular ordering” because
it assumes semantics that are not guaranteed.

Just because a function returns a seemingly predictable value doesn’t mean

it is entirely deterministic. There are lots of vulnerabilities in C/C++
due to reliance on undefined behavior.

We should add an additional guarantee to set/dict that the order returned

will be the same for the same contents regardless of history (but can be
otherwise arbitrary). That will fix the behavior for algorithms like
elementsEqual (i.e. They will return the same result across builds/runs).
It will also implicitly provide as a result, the constraint you were
arguing is needed across copies of a collection type. I agree that that is
an important guarantee. Why not fix both issues with a single
non-source-breaking change?

As I wrote, the behavior of elementsEqual is not broken and does not
require fixing.

Your argument for that is tautological though. You could argue for
keeping any bug in the codebase using the same reasoning.

Why was elementsEqual created? Isn’t it meant to check equality of two
sequences in a generic context where == isn’t available?

No no no no no no no no. That’s precisely why the name is misleading.
elementsEqual is *not* simply a mixed-type version of ==. Remember that ==
as implemented on a concrete sequence type *has no obligation* to use
elementwise comparison using the element’s implementation of ==. This is
not merely theoretical: [Float].== does *not* do an elementwise comparison
using Float.==. By contrast, you are guaranteed a true elementwise
comparison with elementsEqual regardless of how equivalence is defined for
the sequence.

elementsEqual shouldn’t fully imply == of the original type (only that the
elements are “equivalent in the same order” which is what it means for
generic sequences to be equivalent).

But ‘==‘ does NEED to imply elementsEqual. Otherwise you break the
meaning of ‘=='

No, this is not required. For example:

+0.0 == -0.0 but +0.0.sign != -0.0.sign

Equivalence for the purposes of “==“ does *not* require substitutability
for the purposes of all public APIs. The conforming type gets to decide
what aspects are “salient.”

Again, imperfections of modeling and all that.

Isn’t it problematic that two equivalent sets may not be equivalent in a

generic context?

Again, this shows why the name is unfortunate. elementsEqual is not
supposed to be a generic version of ==, and its name is misleading. Repeat:
elementsEqual is *not* an alternative for == in either the generic or
concrete context. Neither one implies the other.

What are you basing this very strong belief on? Has the core team stated
that elementsEqual should not be used to check the equality of sequences?

See Michael Ilseman’s reply for more information on the difference between
the two.

elementsEqual can only provide a notion of equivalence within the generic
context, of course (and not equivalence of the original types overall), but
it is still a notion of equivalence within the context.

No, it is **not** a notion of equivalence of the sequence type, but of the
elements and their iteration order. Again, elementsEqual is poorly named
because **it is not a test of equivalence of the receiver to the argument
in any context whatsoever.**

As I said above (and in another email), ‘==‘ has to imply elementsEqual.

Otherwise you break substitutability.

Again, substitutability for the purposes of == does *not* require
substitutability with respect to all public APIs.

What is the benefit of the guarantee you propose that justifies the

performance cost?

The benefit is that generic algorithms which rely on ordering of
unordered things would be much more robust. It would solve your copy issue
as well.

You are acting like there is a huge performance cost. There isn’t. For
Set, it would still have the traditional performance characteristics for
Sets. Any slowdown (by un-cutting corners) would be amortized over
insertions. (note: I am talking option #3 from my summary here, not #5,
which was the one with an actual performance cost)

I see you skipped responding to this.

It’s kind of beyond the scope of the discussion. What’s not disputed is
that there is a performance cost. The question as to whether the degree of
performance hit can be justified by the improvement in how “robust” the
order-dependent methods on Set become presumes that it is at all a goal to
make them “robust” in any way; I disagree. The reason Set has
order-dependent methods is that it is a necessary consequence of supporting
sequential iteration in a protocol-oriented way. It is currently documented
that a user should choose Set only if they explicitly do not care about the
order of iteration. I see no need to parse degrees of not caring.

Why is the source-breaking change of renaming things better?

Because, in my analysis, the problem is that the method is incorrectly
named. The problem affects all types that conform to Sequence and not just
Set and Dictionary; elementsEqual is a distinct function from ==, and it
must either continue to be distinct or cease to exist, but its name does
nothing to clarify any distinction.

It also won’t fix the original problem you mention as motivation. People
will still be surprised at equivalent sets having different iteration
orders regardless of what the function is named.

That is not the motivating problem. The motivating problem is that people
think that elementsEqual is semantically equivalent to ==. It is a distinct
method that *does not test* equivalence of two sequences.

The motivating problem you originally gave was that people are surprised
when Set([1,2,3]).elementsEqual(Set[3,2,1]) returns false. You believe it
is because they don’t get that elementsEqual works sequentially (on
Sequence),

No, I believe it is because people believe that it is a test of equivalence
of two sets, which it is not.

and I say it is because people expect equivalent sets to produce equivalent

element orders.

The documentation for Set makes it clear that users of the type should have
no such expectation.

···

On Tue, Oct 17, 2017 at 14:41 Jonathan Hull <jhull@gbis.com> wrote:

On Oct 17, 2017, at 11:46 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Oct 17, 2017 at 12:54 Jonathan Hull <jhull@gbis.com> wrote:

On Oct 17, 2017, at 9:34 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Oct 17, 2017 at 09:42 Jonathan Hull <jhull@gbis.com> wrote:

On Oct 17, 2017, at 5:44 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Tue, Oct 17, 2017 at 00:56 Thorsten Seitz <tseitz42@icloud.com> >>> wrote:

Am 17.10.2017 um 00:13 schrieb Xiaodi Wu <xiaodi.wu@gmail.com>:
On Mon, Oct 16, 2017 at 14:21 Thorsten Seitz <tseitz42@icloud.com> >>>> wrote:

Am 16.10.2017 um 16:20 schrieb Xiaodi Wu via swift-evolution < >>>>> swift-evolution@swift.org>:
On Mon, Oct 16, 2017 at 05:48 Jonathan Hull <jhull@gbis.com> wrote:

On Oct 15, 2017, at 9:58 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Sun, Oct 15, 2017 at 8:51 PM, Jonathan Hull <jhull@gbis.com> >>>>>> wrote:

On Oct 14, 2017, at 10:48 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Thanks,
Jon

This is a bug in Float’s implementation of Equatable, rather than a bug in Array. So long as all the elements of Array are correct wrt all the requirements of ==, then the performance optimization that compares buffer pointers shouldn’t have correctness issues like this.

But Float fails to adhere to those rules – specifically reflexivity, because for `let f = 0.0/0.0`, `f == f` returns `false`.

This is the motivation behind the FP part of this proposal for Swift 4, which probably needs another go-around for Swift 5:

(For which I’ll open a new thread, as this one’s plenty long already :)

···

On Oct 18, 2017, at 5:28 AM, Ole Begemann via swift-evolution <swift-evolution@swift.org> wrote:

It also seems to clash with Michael's idea <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20171016/040544.html&gt; that two substitutable sequences should return true for ==.

Therefore having `elementsEqual` as API for unordered collections is a source of bugs …

You keep saying that, and yet after repeated requests to provide evidence you still have not backed up this claim. How many bugs are caused by this? Hypotheticals are not evidence. Show us how much of a problem this is in the real world. Why do you think this is so important to fix? What impact is it going to have? If it weren’t for this thread would you even know the problem existed?

Adam expressed the concern that this required `map` to implemented separately for the split APIs: `Iterable.map` would have to return an `Iterable` whereas `Sequence.map` would have to return a `Sequence`.
This is an independent issue IMO, because currently we have a similar situation: `Collection.map` does not return a `Collection` either. It returns an Array. This is a compromise partly due to missing language features which can simply remain when `Sequence` is split.

That description does not come close to showing the magnitude of the problem with this approach. The reason you think it’s not a significant problem is that you’re only looking at a single function (map). This problem isn’t specific to just one function, though. The problem is that every single function that takes in one Sequence and returns another Sequence would have to handle this distinction, including functions that you and I will want to write and reuse. That means either:

1. Implement the function twice to handle either ordered or unordered.
2. Only handle one type.
3. Implement a complex new language feature that makes #1 slightly easier, but still harder than it is today.

That’s not an improvement. It’s a huge regression in usability. To make that kind of change should require a much stronger argument than “hypothetically this could lead to bugs”.

···

On Oct 18, 2017, at 12:25 PM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

Yup.

···

On Wed, Oct 18, 2017 at 07:28 Ole Begemann <ole@oleb.net> wrote:

On Tue, Oct 17, 2017, at 20:46, Xiaodi Wu via swift-evolution wrote:

On Tue, Oct 17, 2017 at 12:54 Jonathan Hull <jhull@gbis.com> wrote:

Why was elementsEqual created? Isn’t it meant to check equality of two
sequences in a generic context where == isn’t available?

No no no no no no no no. That’s precisely why the name is misleading.
elementsEqual is *not* simply a mixed-type version of ==. Remember that ==
as implemented on a concrete sequence type *has no obligation* to use
elementwise comparison using the element’s implementation of ==. This is
not merely theoretical: [Float].== does *not* do an elementwise comparison
using Float.==. By contrast, you are guaranteed a true elementwise
comparison with elementsEqual regardless of how equivalence is defined for
the sequence.

Really? I would have thought an elementwise comparison is exactly what
[Float].== would do.

I tested this and these are the (very surprising, at least to me) results:

let numbers: [Float] = [1, .nan]
let copy = numbers
let sameNumbers: [Float] = [1, .nan]

numbers == copy // (1) true (!)
numbers == sameNumbers // (2) false (!)
numbers.elementsEqual(copy) // (3) false (ok)
numbers.elementsEqual(sameNumbers) // (4) false (ok)

So [Float].== returns a different result depending on whether you compare
two arrays that share the same buffer (1) or two arrays with the same
contents (2) when the array contains NaN. The reason is that Array.==
immediately returns true if two arrays share the same buffer
<https://github.com/apple/swift/blob/master/stdlib/public/core/Arrays.swift.gyb#L2194-L2197&gt;
.

Even knowing that comparing floats is problematic in the context of NaN
(and in general), I find this very surprising. It also seems to clash with Michael's
idea
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20171016/040544.html&gt;
that two substitutable sequences should return true for ==.

Because, in my analysis, the problem is that the method is incorrectly named. The problem affects all types that conform to Sequence and not just Set and Dictionary; elementsEqual is a distinct function from ==, and it must either continue to be distinct or cease to exist, but its name does nothing to clarify any distinction.

In my analysis, the problem is the method's implementation. As I see it, the only use for `elementsEqual` is as a replacement for `==` when two objects are different types (or not known to be the same)—equal elements, and IF the sequences have an order, in the same order. Could you provide an example where `elementsEqual` randomly returning either true or false depending on internal state alone is a legitimate and desirable result?

It doesn’t randomly return true or false, it consistently returns true or false for the *same* pair of Sequences. What *same* means, of course, is complicated and exists at two levels (as we have two ways of talking about *same*).

It is random (for unordered collections) from the standpoint of business logic.

Could you define what you mean by “business logic”? Is this is a subset of programming in Swift, and if so what is in it? Can users define their own types in this subset? Also, why would arguments made in that subset also apply to all of Swift?

The order of the elements of a Set, while publicly observable, cannot be influenced by business logic because it is an implementation detail.

Yes, this is what Set means when it says: “You use a set instead of an array when you need to test efficiently for membership and you aren’t concerned with the order of the elements in the collection, or when you need to ensure that each element appears only once in a collection.” (emphasis mine)

Therefore it is not possible to write business logic that exploits this order, i.e. it is not possible to write business logic using `elementsEqual` on a Set.
The only exception is using the receiver as argument, i.e. `set.elementsEqual(set)` because in this case, and in this case alone, the result is independent of the effectively random order.
Therefore the single use case for `elementsEqual` for unordered collections is calling `set.elementsEqual(set)` to determine whether a Set of Floats contains NaN values. I claim that this is better written as `set.contains { $0.isNaN }` which works nicely for a Set<Float> and is intention revealing as opposed to using `elementsEqual`.

As I have shown this really is the *single* use case for `elementsEqual` for unordered collections. There can be no other use case.
All other uses of `elementsEqual` involving two different collections where one is an unordered collection have random results that have absolutely no business meaning and therefore are bugs.

Therefore having `elementsEqual` as API for unordered collections is a source of bugs except for a single use case which has a better solution using `contains`. That is why I would like to split the API.

Adam expressed the concern that this required `map` to implemented separately for the split APIs: `Iterable.map` would have to return an `Iterable` whereas `Sequence.map` would have to return a `Sequence`.
This is an independent issue IMO, because currently we have a similar situation: `Collection.map` does not return a `Collection` either. It returns an Array. This is a compromise partly due to missing language features which can simply remain when `Sequence` is split.

Actually the question for the correct return type for `map` is not a simple one. For a `Dictionary` we would probably want to get a `Dicitonary` where the values have been replaced by the mapped ones (this is how Smalltalk does it). But for `Set` we typically do *not* want a `Set` because in general we will be interested in duplicate mapped values. There are times where a `Set` would be the correct return type but this applies to ordered collections or dictionaries as well, so there should be a variant (or an argument with a default) allowing to provide the desired result type or target collection.
But this is a completely separate issue which can be easily deferred until the planned language features have arrived which might help finding a good solution for this question. Until then we will just use the current implementation which returns an array.

To get back to the topic at hand: I propose to differentiate between unordered and ordered collections because I think that this is an important distinction with tractable impact on algorithms (as shown above). The method `elementsEqual` would be part of the ordered sequence protocol and I would suggest the name `elementsPairwiseEqual`.

You are free to propose that and justify its cost. You'll probably want to plan on how you’ll present the cost/benefit analysis for the feedback of “why not just slap a warning on Set/Dictionary?”

···

On Oct 18, 2017, at 12:24 PM, Thorsten Seitz <tseitz42@icloud.com> wrote:

Am 17.10.2017 um 20:47 schrieb Michael Ilseman via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

On Oct 17, 2017, at 10:15 AM, Kevin Nattinger via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

-Thorsten

I apologize for not reading every email in depth in this thread (they are coming in faster than I can parse them), but let me try to present motivation for this and hopefully provide more shared understanding.

We have two forms of equality we’re talking about: equality of Sequence and equality of the elements of Sequences in their respective ordering. `==` covers the former, and I’ll use the existing (harmful) name of `elementsEqual` for the latter.

`==` conveys substitutability of the two Sequences. This does not necessarily entail anything about their elements, how those elements are ordered, etc., it just means two Sequences are substitutable. `elementsEqual` means that the two Sequences produce substitutable elements. These are different concepts and both are independently useful.

Cases:

1. Two Sequences are substitutable and produce substitutable elements when iterated. `==` and `elementsEqual` both return true.

Example: Two arrays with the same elements in the same order.

2. Two Sequences are substitutable, but do not produce substitutable elements when iterated. `==` returns true, while `elementsEqual` returns false.

Example: Two Sets that contain the same elements but in a different order.

Contrived Example: Two Lorem Ipsum generators are the same generator (referentially equal, substitutable for the purposes of my library), but they sample the user’s current battery level (global state) each time they produce text to decide how fancy to make the faux Latin. They’re substitutable, but don’t generate the same sequence.

3. Two Sequences are not substitutable, but produce substitutable elements when iterated. `==` returns false, while `elementsEqual` returns true.

Example: Consider two sequences that have differing identity. `==` operates on an identity level, `elementsEqual` operates at an element level.

Contrived Example: InfiniteMonkeys and Shakespeare both produce the same sonnet, but they’re not substitutable for my library’s purposes.

4. Two Sequences are not substitutable and don’t produce substitutable elements when iterated. `==` and `elementsEqual` both return false.

Example: `[1,2,3]` compared to `[4,5,6]`

It is true that situations #2 and #3 are a little harder to grok, but they are what illustrate the subtle difference at hand. I think situation #2 is the most confusing, and has been the primary focus of this thread as Set exists and exhibits it.

Now, onto naming. `elementsEqual` is a very poor choice of name for the concept of equality of elements in their respective orderings, as it doesn’t highlight the “in their respective orderings” part. `lexicographicallyEqual` highlights the ordering much better, as “abc” is not lexicographically equal to “cba” despite having equal elements. I think it is clearly an improvement over the status quo. I like something a little more explicit (e.g. `elementsOrderedEqual`), personally, but I don’t care that strongly. I’m just glad to see `elementsEqual` getting some clarification.

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

(I saw this after I sent my reply to Benjamin G). We can also deprecate it or warn when the type is concretely known to be Set. Not a total solution, but it’s nice to catch some bugs.

···

On Oct 17, 2017, at 1:21 PM, Manolo van Ee <manolo.vanee@gmail.com> wrote:

On Tue, 17 Oct 2017 at 21:00, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Oct 17, 2017, at 11:47 AM, Michael Ilseman via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

`==` conveys substitutability of the two Sequences. This does not necessarily entail anything about their elements, how those elements are ordered, etc., it just means two Sequences are substitutable. `elementsEqual` means that the two Sequences produce substitutable elements. These are different concepts and both are independently useful.

I agree that ‘==‘ conveys substitutability. Here is the issue:

  let a = Set([1,2,3,4,5])
  let b = Set([5,4,3,2,1])

  a == b //True, they are substitutable

  [1,2,3,4,5].elementsEqual(a) //True
  [1,2,3,4,5].elementsEqual(b) //False… I guess they weren’t actually substitutable after all

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

I read along with most of the thread and like to add one thing. Maybe it’s not the right way to look at things, but I see protocol extensions as being part of any type that conforms to the protocol. As it is now, code completion on a Set will present me with the elementsEqual function, which in my view is misleading, since it cannot be used in any useful way with a Set.

By renaming elementsEqual we might be able to make it less misleading, but it will always be a useless function that is part of the Set namespace (or Dictionary for that matter).

I don’t know what the best solution would be, but to me it feels like distinguishing between Iterable and Sequence, or Ordered and Unordered makes sense, and it might be good to at least investigate what the impact would be.

Regards,
/Manolo

So, what I'm getting from this explanation is that `Equatable` is meaningless on its own; each class needs to document exactly what "substitutability" means as implemented, and any code that uses `==` needs to check the documentation for that specific class and make sure the intended use ("context," as you say) aligns with the class's definition.

Contrived Example: Two Lorem Ipsum generators are the same generator (referentially equal, substitutable for the purposes of my library), but they sample the user’s current battery level (global state) each time they produce text to decide how fancy to make the faux Latin. They’re substitutable, but don’t generate the same sequence.

Evidently I disagree with your definition of "substitutable." How can you say one thing can be substituted for another when doing so gives a different result?

They are substitutable for the purposes of certain operations. The key question is “what” gives a different result.

Sets are primarily about membership, and equal sets are substitutable (hand wavy) for Set-like purposes. But, two equal Sets are not substitutable for iteration purposes unless they produce the same elements in the same order. This latter requirement is far less important than the former for Sets, but can still come up in generic contexts.

Two Lorem Ipsum generators could be equal in that they are substitutable for (hand wavy) faux-Latin generation purposes, even if the sequence of generated characters happens to differ.

Hmm, I… guess I could see randomized generators comparing == because their public configuration is the same, just not the implementation detail of their RNG states. Not sure I'd implement it that way myself—perhaps I just take the "substitutability" more seriously than some.

I'm sorry to bring more bikeshedding, but lexicographicallyEqual seems
absolutely atrocious to me. Imagine all the steps required the user of a
Set<Int> to understand why a lexicographicallyEqual function is suggested
by the compiler ??

Since you seem to give the kind of answer i'm looking for, do you have any
answer to a previous question i asked regarding the rational for conflating
the iterator with the collection ?
What i mean by that is : i think nobody would have cared if the syntax for
Set was actually
set1.iterator.elementEquals(set2.iterator)
It's a bit more verbose, but the meaning is obvious.
I think a reason why people are confused is that the actual design
conflates methods of the "iterator" on the collection type itself. An
alternate design would have been to make collection Sequence*able *rather
than a sequence themselves. But i suppose you've thought about that
already, so i'm just curious to know what made you choose the current
solution instead.

···

On Tue, Oct 17, 2017 at 10:25 PM, Michael Ilseman <milseman@apple.com> wrote:

On Oct 17, 2017, at 12:54 PM, Benjamin G <benjamin.garrigues@gmail.com> > wrote:

Thanks for the post, that's the first clear explanation i see on this
thread for the concepts behind the design for Sequence.

I am a bit afraid that understanding all that is a bit above what to
expect the average swift developer will guess when he sees functions like
"prefix / first / elementEqual (or whatever it's called)" on the Set type.
There is, IMHO, a much higher chance he'll either :
1/ not understand anything, or
2/ think Sets are in fact secretely ordered sets, or start writing generic
extensions above Sequence or Collection thinking those protocols are
synonymous for orderer collections.

1/ is pretty harmless, but 2/ seems like a source of bug.

My personal opinion after reading all this is that we should simply change
the name

Exactly, and that’s what Xiaodi’s proposal does. Confronted with these
complexities, his proposal reasons that a name change is the lessor or
evils, as in the “Proposed solution” section.

to sequentiallyEquals

Xiaodi floated “lexicographicallyEqual” which is accurate and a big
improvement, and I’m liking it more and more. I floated
“sequentiallyEquals”, which I’m liking less and less now. My current
preference is for “elementsOrderedEqual” which I think is more descriptive
but unwieldy. On the other hand, this is a far less common facility, and
order matters, so maybe more verbose names are fine.

How many bugs have been caused by floating point types violating the programmer's mental model of how numbers work? To me, their both in the same category... both involve specific types that claim to adhere to a certain behavior, and both don't in sometimes subtle ways.

- Dave Sweeris

···

On Oct 18, 2017, at 1:04 PM, Adam Kemp via swift-evolution <swift-evolution@swift.org> wrote:

On Oct 18, 2017, at 12:25 PM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

Therefore having `elementsEqual` as API for unordered collections is a source of bugs …

You keep saying that, and yet after repeated requests to provide evidence you still have not backed up this claim. How many bugs are caused by this? Hypotheticals are not evidence. Show us how much of a problem this is in the real world. Why do you think this is so important to fix? What impact is it going to have? If it weren’t for this thread would you even know the problem existed?

Because, in my analysis, the problem is that the method is incorrectly named. The problem affects all types that conform to Sequence and not just Set and Dictionary; elementsEqual is a distinct function from ==, and it must either continue to be distinct or cease to exist, but its name does nothing to clarify any distinction.

In my analysis, the problem is the method's implementation. As I see it, the only use for `elementsEqual` is as a replacement for `==` when two objects are different types (or not known to be the same)—equal elements, and IF the sequences have an order, in the same order. Could you provide an example where `elementsEqual` randomly returning either true or false depending on internal state alone is a legitimate and desirable result?

It doesn’t randomly return true or false, it consistently returns true or false for the *same* pair of Sequences. What *same* means, of course, is complicated and exists at two levels (as we have two ways of talking about *same*).

It is random (for unordered collections) from the standpoint of business logic.

Could you define what you mean by “business logic”? Is this is a subset of programming in Swift, and if so what is in it? Can users define their own types in this subset? Also, why would arguments made in that subset also apply to all of Swift?

From Wikipedia: "In computer software <https://en.wikipedia.org/wiki/Software&gt;, business logic or domain logic is the part of the program that encodes the real-world business rules <https://en.wikipedia.org/wiki/Business_rule&gt; that determine how data can be created, stored, and changed <https://en.wikipedia.org/wiki/Create,_read,_update_and_delete&gt;\. It is contrasted with the remainder of the software that might be concerned with lower-level details of managing a database <https://en.wikipedia.org/wiki/Database&gt; or displaying the user interface <https://en.wikipedia.org/wiki/User_interface&gt;, system infrastructure, or generally connecting various parts of the program.“
(https://en.wikipedia.org/wiki/Business_logic\)

I explained why there is no possibility to use `elementsEqual` with unordered collections like Set except for the single use case of comparing the elements of a set with themselves, i.e. `set.elementsEqual(set)`.

The order of the elements of a Set, while publicly observable, cannot be influenced by business logic because it is an implementation detail.

Yes, this is what Set means when it says: “You use a set instead of an array when you need to test efficiently for membership and you aren’t concerned with the order of the elements in the collection, or when you need to ensure that each element appears only once in a collection.” (emphasis mine)

Exactly. I am well aware that Sets are unordered. This whole discussion revolves around `elementsEqual` being not appropriate for unordered collections like Set.

-Thorsten

···

Am 18.10.2017 um 22:36 schrieb Michael Ilseman <milseman@apple.com>:

On Oct 18, 2017, at 12:24 PM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:

Am 17.10.2017 um 20:47 schrieb Michael Ilseman via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

On Oct 17, 2017, at 10:15 AM, Kevin Nattinger via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Therefore it is not possible to write business logic that exploits this order, i.e. it is not possible to write business logic using `elementsEqual` on a Set.
The only exception is using the receiver as argument, i.e. `set.elementsEqual(set)` because in this case, and in this case alone, the result is independent of the effectively random order.
Therefore the single use case for `elementsEqual` for unordered collections is calling `set.elementsEqual(set)` to determine whether a Set of Floats contains NaN values. I claim that this is better written as `set.contains { $0.isNaN }` which works nicely for a Set<Float> and is intention revealing as opposed to using `elementsEqual`.

As I have shown this really is the *single* use case for `elementsEqual` for unordered collections. There can be no other use case.
All other uses of `elementsEqual` involving two different collections where one is an unordered collection have random results that have absolutely no business meaning and therefore are bugs.

Therefore having `elementsEqual` as API for unordered collections is a source of bugs except for a single use case which has a better solution using `contains`. That is why I would like to split the API.

Adam expressed the concern that this required `map` to implemented separately for the split APIs: `Iterable.map` would have to return an `Iterable` whereas `Sequence.map` would have to return a `Sequence`.
This is an independent issue IMO, because currently we have a similar situation: `Collection.map` does not return a `Collection` either. It returns an Array. This is a compromise partly due to missing language features which can simply remain when `Sequence` is split.

Actually the question for the correct return type for `map` is not a simple one. For a `Dictionary` we would probably want to get a `Dicitonary` where the values have been replaced by the mapped ones (this is how Smalltalk does it). But for `Set` we typically do *not* want a `Set` because in general we will be interested in duplicate mapped values. There are times where a `Set` would be the correct return type but this applies to ordered collections or dictionaries as well, so there should be a variant (or an argument with a default) allowing to provide the desired result type or target collection.
But this is a completely separate issue which can be easily deferred until the planned language features have arrived which might help finding a good solution for this question. Until then we will just use the current implementation which returns an array.

To get back to the topic at hand: I propose to differentiate between unordered and ordered collections because I think that this is an important distinction with tractable impact on algorithms (as shown above). The method `elementsEqual` would be part of the ordered sequence protocol and I would suggest the name `elementsPairwiseEqual`.

You are free to propose that and justify its cost. You'll probably want to plan on how you’ll present the cost/benefit analysis for the feedback of “why not just slap a warning on Set/Dictionary?”

-Thorsten

I apologize for not reading every email in depth in this thread (they are coming in faster than I can parse them), but let me try to present motivation for this and hopefully provide more shared understanding.

We have two forms of equality we’re talking about: equality of Sequence and equality of the elements of Sequences in their respective ordering. `==` covers the former, and I’ll use the existing (harmful) name of `elementsEqual` for the latter.

`==` conveys substitutability of the two Sequences. This does not necessarily entail anything about their elements, how those elements are ordered, etc., it just means two Sequences are substitutable. `elementsEqual` means that the two Sequences produce substitutable elements. These are different concepts and both are independently useful.

Cases:

1. Two Sequences are substitutable and produce substitutable elements when iterated. `==` and `elementsEqual` both return true.

Example: Two arrays with the same elements in the same order.

2. Two Sequences are substitutable, but do not produce substitutable elements when iterated. `==` returns true, while `elementsEqual` returns false.

Example: Two Sets that contain the same elements but in a different order.

Contrived Example: Two Lorem Ipsum generators are the same generator (referentially equal, substitutable for the purposes of my library), but they sample the user’s current battery level (global state) each time they produce text to decide how fancy to make the faux Latin. They’re substitutable, but don’t generate the same sequence.

3. Two Sequences are not substitutable, but produce substitutable elements when iterated. `==` returns false, while `elementsEqual` returns true.

Example: Consider two sequences that have differing identity. `==` operates on an identity level, `elementsEqual` operates at an element level.

Contrived Example: InfiniteMonkeys and Shakespeare both produce the same sonnet, but they’re not substitutable for my library’s purposes.

4. Two Sequences are not substitutable and don’t produce substitutable elements when iterated. `==` and `elementsEqual` both return false.

Example: `[1,2,3]` compared to `[4,5,6]`

It is true that situations #2 and #3 are a little harder to grok, but they are what illustrate the subtle difference at hand. I think situation #2 is the most confusing, and has been the primary focus of this thread as Set exists and exhibits it.

Now, onto naming. `elementsEqual` is a very poor choice of name for the concept of equality of elements in their respective orderings, as it doesn’t highlight the “in their respective orderings” part. `lexicographicallyEqual` highlights the ordering much better, as “abc” is not lexicographically equal to “cba” despite having equal elements. I think it is clearly an improvement over the status quo. I like something a little more explicit (e.g. `elementsOrderedEqual`), personally, but I don’t care that strongly. I’m just glad to see `elementsEqual` getting some clarification.

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

The warning will have to be slapped on `Sequence` because I and others can write types adopting Sequence but being unordered.

And it will have to be slapped on every public generic function written by anyone which takes a Sequence as argument and internally uses `elementsEqual` on that argument, so that the user of such an API knows that he cannot use that function with a Set or another unordered subtype of Sequence.

-Thorsten

···

Am 18.10.2017 um 22:36 schrieb Michael Ilseman <milseman@apple.com>:

To get back to the topic at hand: I propose to differentiate between unordered and ordered collections because I think that this is an important distinction with tractable impact on algorithms (as shown above). The method `elementsEqual` would be part of the ordered sequence protocol and I would suggest the name `elementsPairwiseEqual`.

You are free to propose that and justify its cost. You'll probably want to plan on how you’ll present the cost/benefit analysis for the feedback of “why not just slap a warning on Set/Dictionary?”