[Repo Assist] fix: PersistentHashMap.containsKey returns true for None/unit/null values#238
Conversation
…t values ContainsKey was comparing the result of INode.find (which returns the value as obj) against null. This caused false negatives when the stored value is null at the .NET level — which happens for None, unit, and explicitly null values. Fix: use INode.tryFind (which already existed on all node types) in ContainsKey for both PersistentHashMap and TransientHashMap. tryFind returns an option, so Some null (i.e. key found, value is null) is correctly distinguished from None (key not found). Activate the two previously-pending tests and add three new tests covering None, unit, and mixed Some/None scenarios. Closes #85 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/repo-assist format the code according to AGENTS.md |
|
/repo-assist This is not the right tradeoff:
Instead you should indeed have a dedicated containsKey returning bool. Performance matters for collections! |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… allocation Replace the tryFind-based ContainsKey with a new containsKey method on INode that returns bool directly. This avoids allocating an 'option' discriminated union on every ContainsKey call, improving performance for this hot path. Implement containsKey on all three INode types: - HashCollisionNode: check findIndex >= 0 and key equality - ArrayNode: delegate to child node.containsKey - BitmapIndexedNode: check bitmap bit, then key equality or recurse into sub-node Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Commit pushed:
|
This comment has been minimized.
This comment has been minimized.
|
Commit pushed:
|
|
🤖 This is an automated response from Repo Assist. Done! I've also addressed your performance feedback. I've added a dedicated All 710 tests pass (same as before, 4 pre-existing skips).
|
🤖 This PR was created by Repo Assist, an automated AI assistant.
Fixes the long-standing bug reported in #85:
PersistentHashMap.containsKey(and theTransientHashMapcounterpart) returnedfalsefor keys whose associated value isnullat the .NET level — includingNone,unit, and explicitnull.Root Cause
ContainsKeycalledINode.find, which returns the stored value asobj. The result was compared tonullto detect presence:When the stored value is
null(as it is forNone,unit, or anynull-valued reference type), this comparison yieldsfalseeven though the key is present.Fix
INodealready had atryFindmethod on all node types, returningobj option.Some nullis correctly distinguished fromNone, so we changeContainsKeyto use it:Applied to both
PersistentHashMap<'T,'S>.ContainsKeyandTransientHashMap<'T,'S>.ContainsKey.Tests
ptest(pending) tests forNonevalues in bothPersistentHashMapTest.fsandTransientHashMapTest.fs.PersistentHashMapTest.fs:containsKeywithunitvaluecontainsKeyafter multiple adds mixingNoneandSomeTrade-offs
tryFindallocates anoptiondiscriminated union on everyContainsKeycall. In the common case (non-null values), this is a minor allocation cost. The alternative — adding a dedicatedcontainsKeymethod toINodereturningbool— would be more performant but requires more changes; this minimal fix is correct and low-risk.Test Status
✅ All 710 tests pass (
dotnet test—Passed: 710, Skipped: 4, Failed: 0). The skipped tests are pre-existing skips unrelated to this change.Closes #85