Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Map#updateWith to modify a map based on current value #7320

Closed
wants to merge 1 commit into from

Conversation

exoego
Copy link

@exoego exoego commented Oct 6, 2018

Addresses scala/bug#11185 by adding the following methods that is analogus to java.util.Map.compute

- compute
updateWith
computed
updatedWith
Added to mutable.Map
concurrent.Map
immutable.Map
Return value New value Option[V] Updated Map[K,V]

@scala-jenkins scala-jenkins added this to the 2.13.0-RC1 milestone Oct 6, 2018
*
* @param key the key value
* @param remappingFunction the function to compute a new mapping
* @tparam V1 the type of newly computed value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

V1 does not appear to be used here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, removed.

@exoego exoego force-pushed the map-compute branch 3 times, most recently from d6f09d1 to 3f68a43 Compare October 6, 2018 12:17
@julienrf
Copy link
Contributor

julienrf commented Oct 6, 2018

I’m not sure compute is a meaningful name (except for people familiar with the Java API). FTR, in scala-collection-contrib it’s named updateWith (updatedWith for the immutable version).

@diesalbla
Copy link
Contributor

diesalbla commented Oct 6, 2018

If it serves as an inspiration, in a Haskell library there is a similar method called alter.

@exoego
Copy link
Author

exoego commented Oct 6, 2018

IMO, this method should have a name that differ from mutable.Map#update and immutable.Map#updated. In contrast with update/updated that can only add or overwrite the key-value pair, this method can also remove the existing pair. In short, update + remove = new name.

alter may be good.
compute is my preference since I have a background of Java.

@NthPortal
Copy link
Contributor

An interesting question is whether or not the argument should be a function (as it is now) or partial function (as it is in collection-contrib, linked by @julienrf)

@SethTisue SethTisue added WIP and removed WIP labels Nov 6, 2018
@SethTisue
Copy link
Member

SethTisue commented Nov 6, 2018

I don't think we should call it compute, in many cases I'd be willing to just follow a Java precedent, but compute is an astonishingly terrible name, you could call practically anything compute

I think updateWith/updatedWith is fine

@SethTisue
Copy link
Member

SethTisue commented Nov 6, 2018

An interesting question is whether or not the argument should be a function (as it is now) or partial function

what are the arguments either way?

P.S. needs rebase
P.P.S. s/Abesent/Absent/g

@julienrf
Copy link
Contributor

julienrf commented Nov 6, 2018

what are the arguments either way?

Version with a partial function

Definition site:

def updatedWith(key: K)(pf: PartialFunction[Option[V], Option[V]]): Map[K, V]

Call site:

map.updatedWith("foo") { case Some(previous) => Some(previous * 2) }

Version with a total function

Definition site:

def updatedWith(key: K)(pf: Option[V] => Option[V]): Map[K, V]

Call site:

map.updatedWith("foo") {
  case Some(previous) => Some(previous * 2)
  case None => None
}

The version with a partial function has a more complex type signature, but allows users to write less code at use site. Conversely, the version with a total function has a simpler type signature but requires users to always specify the cases they want to ignore.

Both versions are fine to me.

@NthPortal
Copy link
Contributor

I agree with @julienrf - both are fine to me as well. I have a weak preference for the one which uses a PartialFunction, but would consider that outweighed if anyone has any performance arguments

@NthPortal
Copy link
Contributor

I am okay with updateWith/updatedWith - despite the fact that update/updated cannot remove elements, I still think it is fairly intuitive. alter/altered is also a possibility, though I'm concerned that it is less intuitive. I would probably lean towards the former

@exoego exoego changed the title Add Map#compute to modify a map based on current value WIP: Add Map#compute to modify a map based on current value Nov 7, 2018
@exoego exoego changed the title WIP: Add Map#compute to modify a map based on current value Add Map#updateWith to modify a map based on current value Nov 7, 2018
@exoego
Copy link
Author

exoego commented Nov 7, 2018

I rebased the PR and renamed compute to updateWith.

Total function is used instead of PartialFunction.
IMHO, PartialFunction complexify updateWith. PF allows users not to write unnecessary case, but users should understand what happens in case of ignored case (e.g. MatchError thrown? or Existing value returned?).Total function version is easier for users to imagine and understand the behavior of updateWith.
Total functionalso allow users to write less code with underscore, e.g. _.orElse(Some(...)) or something like that.

@SethTisue SethTisue added the library:collections PRs involving changes to the standard collection library label Nov 8, 2018
val previousValue = this.get(key)
val nextValue = remappingFunction(previousValue)
(previousValue, nextValue) match {
case (None, None) => this.asInstanceOf[CC[K,V1]]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.asInstanceOf[CC[K,V1]] should be coll

val nextValue = remappingFunction(previousValue)
(previousValue, nextValue) match {
case (None, None) => this.asInstanceOf[CC[K,V1]]
case (Some(_), None) => this.remove(key).asInstanceOf[CC[K,V1]]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had a similar case recently. It would be good to restrict C <: CC[K, V] to get rid of this cast. I think I tried such a change a while ago but ran into some problems. It's worth another try.

@julienrf
Copy link
Contributor

@exoego Would you have some time to address the last reviews?

@exoego
Copy link
Author

exoego commented Dec 11, 2018

@exoego Would you have some time to address the last reviews

I took several hours but still could not fix generics constraints. I think It is too complex for non-expert of scala collections.
I am ok if someone take over this PR .

@julienrf
Copy link
Contributor

julienrf commented Dec 11, 2018

@exoego Yeah, it might be better to not change type bounds… I’ve pushed an update of the PR, rebased, and with Stefan’s comments addressed, in #7517 (I had to open another PR because I can not push force to your repo…). Thanks again for your contribution!

@szeiger
Copy link
Contributor

szeiger commented Dec 11, 2018

Yes, ignore the type bounds. I gave it another try and didn't get any further than #7504. See my comments there on what the remaining problems are.

@szeiger
Copy link
Contributor

szeiger commented Dec 14, 2018

I merged the updated version in #7517

@szeiger szeiger closed this Dec 14, 2018
@SethTisue SethTisue removed this from the 2.13.0-RC1 milestone Feb 22, 2019
@exoego exoego deleted the map-compute branch June 27, 2019 04:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
library:collections PRs involving changes to the standard collection library
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants