-
Notifications
You must be signed in to change notification settings - Fork 65
Description
The current implementation of sets and maps being 'ordered by default' is unintuitive as it isn't actually 'by default'; One would expect a persistent set to count as a persistent set, regardless of its order-ness.
val ordered = persistentSetOf("")
val unordered = persistentHashSetOf("")
check(ordered === ordered.toPersistentSet()) // true
check(ordered === ordered.toPersistentHashSet()) // false
check(unordered === unordered.toPersistentSet()) // false, what
check(unordered === unordered.toPersistentHashSet()) // true
Additionally, why do ordered sets even exist? Isn't the whole point of sets that they're unordered, unlike lists? Regardless, why are they default? Why are ordered maps the default too, for that matter? They're slower, and orderness isn't a part of maps' and sets' contracts; sure, it is a nice option sometimes, but then you could just explicitly use ordered variants; but for most use cases, wouldn't it make more sense to prioritize speed over a feature that isn't a part of the contract?
My solution
I'm assuming in both that the soon-to-be released beta will break backwards compatibility completely, as #185 talks mainly about public API changes. An educated guess tells me this is because this library is being merged into the stdlib, which means we can change the functionality of existing methods.
- Rename the regular persistent builders to
orderedPersistent
(toOrderedPersistentSet
,orderedPersistentMapOf()
, etc.; orpersistentOrdered
, doesn't really matter). - Re-add the
persistent
builders (for both set and map; this is just an example), except for a tiny difference in implementation; Instead of:Have:public fun <T> Iterable<T>.toPersistentSet(): PersistentSet<T> = this as? PersistentOrderedSet<T> ?: (this as? PersistentOrderedSetBuilder)?.build() ?: PersistentOrderedSet.emptyOf<T>() + this
public fun <T> Iterable<T>.toPersistentSet(): PersistentSet<T> = this as? PersistentSet<T> // changed here ?: (this as? PersistentSetBuilder)?.build() ?: PersistentOrderedSet.emptyOf<T>() + this // imo should be changed to hash but doesn't really matter
- Keep the hash builders as-is.
TL;DR of the solution:
val ordered = persistentOrderedSetOf("")
val unordered = persistentHashSetOf("")
check(ordered === ordered.toPersistentOrderedSet()) // true
check(ordered === ordered.toPersistentHashSet()) // false
check(unordered === unordered.toPersistentOrderedSet()) // false, ok this time
check(unordered === unordered.toPersistentHashSet()) // true
// and also
check(ordered === ordered.toPersistentSet()) // true
check(unordered === unordered.toPersistentSet()) // true
As for "but what about other implementations?" --
- Make all immutable interfaces
sealed
to prevent implementations that allow mutations #147 - You could add
PersistentOrdered
andPersistentHash
(or immutable versions of them) interfaces; but it's impossible this symmetric with mutable collections without hacking the type system even further, because of Java.