Skip to content

Preserve null policy in wrapped Java Maps#10129

Merged
lrytz merged 1 commit intoscala:2.13.xfrom
som-snytt:followup/12586-preserve-NPE
Sep 13, 2022
Merged

Preserve null policy in wrapped Java Maps#10129
lrytz merged 1 commit intoscala:2.13.xfrom
som-snytt:followup/12586-preserve-NPE

Conversation

@som-snytt
Copy link
Copy Markdown
Contributor

When using compute, which has "remove" semantics for null value,
try alternative means when user wants to put a null. In particular,
if the underlying map wants to throw NPE, the fallback should do so.

Follow-up to #10027

@scala-jenkins scala-jenkins added this to the 2.13.10 milestone Sep 3, 2022
@SethTisue SethTisue requested a review from a team September 3, 2022 15:56
@SethTisue SethTisue added the library:collections PRs involving changes to the standard collection library label Sep 3, 2022
override def getOrElseUpdate(key: K, op: => V): V = underlying.computeIfAbsent(key, _ => op)
override def getOrElseUpdate(key: K, op: => V): V =
underlying.computeIfAbsent(key, _ => op) match {
case null => super.getOrElseUpdate(key, op)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

would be good to clarify super[Which] here (and below)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see, super[concurrent.Map].getOrElseUpdate is not allowed, interesting language limitation.

Copy link
Copy Markdown
Member

@lrytz lrytz left a comment

Choose a reason for hiding this comment

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

It seems null handling of JConcurrentMapWrapper.putIfAbsent is not aligned with Scala's semantics, if the underlying map contained a binding with value null, it will return None. Would it make sense to fix this?

I guess it's not too important as both concurrent map implementations in the JDK don't support null. Is there a known / commonly used one that does?

LGTM

override def getOrElseUpdate(key: K, op: => V): V = underlying.computeIfAbsent(key, _ => op)
override def getOrElseUpdate(key: K, op: => V): V =
underlying.computeIfAbsent(key, _ => op) match {
case null => super.getOrElseUpdate(key, op)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see, super[concurrent.Map].getOrElseUpdate is not allowed, interesting language limitation.

}
try Option(underlying.compute(key, remap))
catch {
case PutNull => super.updateWith(key)(remappingFunction)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

So the dance is between

  • supporting Scala semantics for updateWith (can put Some(null))
  • supporting the underlying map's contract (the ConcurrentHashMap.compute method evaluates the argument function only once)

and we can't have both, so for Some(null) the function will re-evaluate. That looks fine to me.

@lrytz lrytz force-pushed the followup/12586-preserve-NPE branch from 24d883c to 094d970 Compare September 12, 2022 12:05
@SethTisue SethTisue modified the milestones: 2.13.10, 2.13.9 Sep 12, 2022
@lrytz lrytz force-pushed the followup/12586-preserve-NPE branch from 4d1f8c8 to 7abe9b8 Compare September 13, 2022 07:53
@lrytz
Copy link
Copy Markdown
Member

lrytz commented Sep 13, 2022

@som-snytt I pushed a commit to remove the getOrElseUpdate and updateWith overrides from non-concurrent map wrappers. Let me know if I missed something, if there was actually a reason for them besides making the wrapper implementations more aligned.

@som-snytt
Copy link
Copy Markdown
Contributor Author

I think the non-concurrent case was for efficiency.

When using `compute`, which has "remove" semantics for `null` value,
try alternative means when user wants to put a `null`. In particular,
if the underlying map wants to throw NPE, the fallback should do so.
@lrytz lrytz force-pushed the followup/12586-preserve-NPE branch from 7abe9b8 to b824b84 Compare September 13, 2022 12:02
@lrytz
Copy link
Copy Markdown
Member

lrytz commented Sep 13, 2022

Ah, that's a good reason 👍 thanks.

@lrytz lrytz merged commit 986dcc1 into scala:2.13.x Sep 13, 2022
@SethTisue SethTisue added the release-notes worth highlighting in next release notes label Sep 13, 2022
@som-snytt som-snytt deleted the followup/12586-preserve-NPE branch September 13, 2022 15:33
@SethTisue SethTisue changed the title Preserve null policy in wrapped Java Map Preserve null policy in wrapped Java Maps Sep 15, 2022
hamzaremmal pushed a commit to hamzaremmal/scala3 that referenced this pull request May 2, 2025
…eserve-NPE

Preserve null policy in wrapped Java Map
hamzaremmal pushed a commit to scala/scala3 that referenced this pull request May 7, 2025
…eserve-NPE

Preserve null policy in wrapped Java Map
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 release-notes worth highlighting in next release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants