"nondeterministic order" annotations to make it explicit in the code when the order of something is not deterministic (e.g., when converting a set to a list)

So if you have a set s say, you would need to use something like Array(s.nondeterministicOrder) to convert it to an array.

It's very easy to forget when an order is nondeterministic and run into strange bugs because your code depends on deterministic order.

If you need a set with elements in a well-defined order, consider using OrderedSet from the Swift Collections library.

It's not the conversion from a set to an array that causes you a problem if you "forget" that Set isn't an ordered collection type—indeed, that conversion actually fixes the element order—but rather the problem is that your value was an instance of Set to begin with.

2 Likes

But the conversion doesn't fix the element order. You could get different orders from the same set.

An array holds its elements in a well-defined order; if you convert a set to an array, then you no longer have an issue with the order of elements being arbitrary.

Also, note that if you do not mutate a value of type Set, you can iterate over it any number of times and you will always get the elements in the same order.

Iteration over a set is not deterministic. You can get different results even when the executions up to that point are identical.

Allow me to be very precise in my meaning:

Within the same execution of a program, given a unique, immutable binding s to a value of type Set—that is, let s = Set(...)—it is guaranteed that for _ in 0..<x { print(s.first) } prints the same element x times.

But if you have something like:

let s = Set([1,2,3,4,5,6,7,8,9,10])
let s2 = Set([1,2,3,4,5,6,7,8,9,10])

Then the order is usually different for s and s2. This can result in hard to debug issues because it may not be something you expect/remember.

An explicit annotation would remind you if this and also allow you to search for nondetermistic orderings in your code.

See my first reply: if you're expecting to use a ordered collection, then consider OrderedSet; the error is in creating a value of type Set, not in converting the value to an array.

4 Likes

I don't want to use external libraries.

Moreover, the problem is more about writing code that results in hard to debug issues.

If you are constantly being reminded of this behavior via explicit annotations in conversions/iteration, then you would write code that works correctly given this behavior.

Any usage of a Set is a perfect indicator of an arbitrary ordering of values. You can just search for usages of Set rather than needing some additional annotation. If you’re worried about the ordering of values in a Set to begin with that is likely a sign you should use an OrderedSet instead as mentioned.

3 Likes

The use of Set is not necessarily an error. But conversions/iterations on sets can introduce hard to debug problems. That's why the explicit annotation would go where you do the conversion/iteration.

I would suggest that if the ordering of values is important to maintain then using a plain old Set would indeed be an programmer error since that data type does not provide such a guarantee.

8 Likes

It may not be obvious that the ordering of values is important though. Having explicit annotations would make you think of this issue more.

Your confusion is really caused by the fact that you're expecting a certain API to behave opposite to what it explicitly states in the documentation. A Set never claims to be ordered, and this semantics is common across virtually every popular programming language.

Do you have any technical reason for this? "Collections" is a sibling library to the Swift project; it being external has very minimal implications that aren't very likely to cause you any issues. If anything, most cases encourage the use of such libraries, because those are much less rigid and can receive updates and API extensions much quicker, as opposed to the Standard Library.

Ok, but what are you going to do with that? Say, you're reminded of the randomised order; how will it actually help you fix the bugs you're taking about?

1 Like

If the boundaries of where sorting is important are not clear in your codebase then someone may knowingly choose to use Array(s.nondeterministicOrder) thinking it won’t cause any issues, so it doesn’t really fix the problem.

I don't trust external libraries as much as I trust what Apple ships as part of Xcode.

Also, nondeterministic Set behavior is something that Apple introduced in a later version of Swift, so not everyone knows about it and even those who do know about it could temporarily forget it while coding.

Constant reminders via explicit annotations will help you keep this issue in mind and avoid hard to debug problems.

Also, you can use a search tool to find nondeterministic orderings in your code via the explicit annotations when you suspect a bug might be caused by one or more of them.

you have far too much trust in what Apple ships with XCode :slight_smile:

but i agree with you that introducing a swift-collections dependency is probably overkill. as many others in this thread have mentioned, your data should probably be stored in an Array. you can wrap this array in a user-defined type that also stores a private Set that it keeps in sync with mutations on the array.

1 Like

"Collections" is literally a library maintained by Apple.

Also, on a more philosophical note, trust alone won't help you avoid issues — tests and rigorous metrics can. If you are unsure if your case is properly handled, you can either write a test yourself or look into the existing test suite. "Collections" is an extremely high-quality package written by highly qualified people, so you really have to look hard for a reason to not trust it in the first place, if that's what you're going with.

Sure, but to a certain extent. You have to strike a balance between necessary clarity and being outright obtuse for the idealistic sake of writing a selfDocumentingInEveryImaginableAspectMethod().

Again, let's imagine you've found such a bug this way. What's next? You most likely chose a Set over an array in the first place to get O(1) operations (otherwise — why?). Now, if you found that kind of bug, you'll have to revert everything back to an array or otherwise rewrite your code to keep track of the ordering, but then you can almost surely forget about the O(1) performance. What was the reason for choosing a Set in the first place then?

4 Likes

I am getting old and have seen this sort of thread a lot. Someone uses the pretense of being irritated about the usability of some tool, due to poor quirk discoverability, but is actually looking to use the mnemonic capabilities of argument with other people to cement whatever needs to be remembered in order for the tool to virtually have improved usability. Does this trope have a name?

1 Like

it don’t know if it has a name, but i would say if it helps someone learn swift better, then the forums are functioning as intended…

3 Likes