-
Notifications
You must be signed in to change notification settings - Fork 72
Add Map operations. #137
Add Map operations. #137
Conversation
def iterableFactory: IterableFactory[Set] = Set | ||
def empty: Set[K] = iterableFactory.empty | ||
protected[this] def fromSpecificIterable(coll: Iterable[K]): Set[K] = iterableFactory.fromIterable(coll) | ||
protected[this] def newSpecificBuilder(): Builder[K, Set[K]] = iterableFactory.newBuilder() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It’s a bit unfortunate that we have to implement all these methods.
* @return an immutable map consisting only of those key value pairs of this map where the key satisfies | ||
* the predicate `p`. The resulting map wraps the original map without copying any elements. | ||
*/ | ||
def filterKeys(p: K => Boolean): CC[K, V @uncheckedVariance] = mapFromIterable(View.FilterKeys(coll, p)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that filterKeys
and mapValues
are not anymore lazy.
BTW, it’s good to see that their implementation is simpler than in the current collections!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have to rename them, then. A difference in behavior like that can silently break code in unpleasant ways. It's annoying that they were always lazy, but we have to allow the user an opportunity to have every single spot pointed out to them. (We can auto-rewrite the ones that were obviously lazy-version-then-immediately-something-eager.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise, if we implement a lazy semantics what do you think of changing their return type to View[(K, V)]
instead of CC[K, V]
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switching it to a view isn't good either because it no longer has map semantics. You could switch it to a map view if we had such a thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can do .to(Map)
to get back Map
semantics. Or did you mean something else by “map semantics”?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, that doesn't work because it's not lazy any more. The output of filterKeys
and mapValues
is, for better or worse, a lazy map. Lazy-but-not-a-map is not a map. Map-but-not-lazy is not lazy. We can't know what properties people have relied upon in their code, so the only reasonable approach is to deprecate or remove the entire method and introduce another that does something that we think is less confusing, or maintain both the laziness and the map-ness.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think returning a View
is fine, if people rely on Map
-specific methods then they’ll get a compile error. I think the fact that filterKeys
and mapValues
return a “lazy Map” in the current collections can be a source of confusion and we should not support that in the strawman. Moreover, in the strawman we have a cleaner distinction between lazy and strict collections so we should take advantage of that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we're taking away the functionality completely, without a deprecation and no way to recover it, and not even changing the method name? Maybe it will be okay because nobody's using them for what they can do, but otherwise this is precisely the kind of thing that tends to upset users (unannounced breaking change with no workaround).
* @return a new $coll that contains all elements of the current $coll | ||
* except one less occurrence of each of the elements of `elems`. | ||
*/ | ||
def removeAll(keys: IterableOnce[K]): C = fromSpecificIterable(keys.iterator().foldLeft(coll)(_ - _)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dotty reports a type error here:
def removeAll(keys: IterableOnce[K]): C = fromSpecificIterable(keys.iterator().foldLeft(coll)(_ - _))
^^^^^
found: C
required: CC[K, V]
But I think he is wrong.
Indeed, the type of coll
is CC[K, V]
, which is upper bounded by MapOps[K, V, CC, CC[K, V]]
(note that the last parameter (whose formal name is C
) is instantiated to CC[K, V]
). Then, the -
operator returns a C
, which is a CC[K, V]
.
cc @odersky.
@@ -34,7 +34,7 @@ trait MapOps[K, +V, +CC[X, +Y] <: Map[X, Y] with MapOps[X, Y, CC, CC[X, Y]], +C | |||
* @return a new $coll that contains all elements of the current $coll | |||
* except one less occurrence of each of the elements of `elems`. | |||
*/ | |||
def removeAll(keys: IterableOnce[K]): C = fromSpecificIterable(keys.iterator().foldLeft(coll)(_ - _)) | |||
def removeAll(keys: IterableOnce[K]): C = fromSpecificIterable(keys.iterator().foldLeft[CC[K, V]](coll)(_ - _)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this commit I try an alternative solution but I still get an error (different, though), with Dotty:
def removeAll(keys: IterableOnce[K]): C = fromSpecificIterable(keys.iterator().foldLeft[CC[K, V]](coll)(_ - _))
^^^
value `-` is not a member of CC[K, V]
Which, again, seems wrong to me: CC
is upper bounded by MapOps
, which provides the -
method.
1351bc9
to
8df5083
Compare
I’ll leave this PR for later because it has several compilation errors with dotty. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are reasons to have lazy mapValues
method?
Current implementations don't distinguish view
and don't rehash keys
functionalities, what probably should not go as default. There are several cases where strict implementation (mapValuesNow
) would have advantage. Why don't make it default behaviour?
@Dveim - The reason is to allow the strawman to function as a drop-in replacement for the existing collections. This has been a primary goal all along. Since the behavior changes radically when it is lazy vs. eager, simply swapping one for another isn't something you can do blind, even if it compiles. A lazy |
Thanks for answer. Indeed, compatibility alone is enough to leave things as
is.
Do you plan to add strict version, `mapValuesNow` ?
|
If we keep the lazy behavior of Then, do we want a strict version? We can always write Also, I didn’t carefully check but I think in my projects my main usage of |
I rebased to master and bumped the dotty version to take advantage of scala/scala3#2889 but we now have other compilation errors:
|
Dotty fix in progress: scala/scala3#2914 😄 |
No description provided.