Skip to content

Add pop for sets and maps #1134

Open
Open
@meooow25

Description

@meooow25

I propose adding

pop :: Ord k => k -> Map k a -> Maybe (a, Map k a)
pop :: Ord a => a -> Set a -> Maybe (Set a)
pop :: Int -> IntMap a -> Maybe (a, IntMap a)
pop :: Int -> IntSet -> Maybe IntSet

Returns a Nothing if the key is absent. Otherwise, returns a new set or map with the key removed, and the value in case it is a map.

Previously proposed in #757 (with Maybe on the inside). I agree with the reasons there.

  • They're useful functions I expected to be in Data.Map and Data.IntMap. (This might be influenced by the fact that they're defined on Python's dict.)
  • Their implementations (~ updateLookupWithKey (\_ _ -> Nothing)) is harder to parse than a simple pop, which should help Haskell codebases become a bit cleaner :).
  • Their implementations are a bit non-obvious. My first instinct was to write (Map.lookup ..., Map.delete ...), which would have done two traversals. Having "properly" implemented functions in the lib would prevent people from writing their own suboptimal ones.

Some more reasons:

  • k -> Map k a -> Maybe (a, Map k a) is a closer inverse of insert than delete.
  • updateLookupWithKey (\_ _ -> Nothing) is inefficient because 1. it takes a function which is unnecessary and 2. it always constructs a new map/set.

Previously, there were some concerns about whether this is a user-demanded feature on the libraries thread (e.g. here), and the answer is yes! Hackage Search reveals quite a few results: https://hackage-search.serokell.io/?q=%5C.updateLookupWithKey%5Cs. Some examples:

I expect there are more instances doing lookup+delete, and it's harder to search for those.

Naming

Some suggestions were:

  • pop - seems fine to me
  • extract - doesn't make much sense for sets
  • lookupDelete - long name, also doesn't make much sense for sets. Sets would need memberDelete.

Implementation

There are two ways to implement this.

  • Simple: Go down the map/set and back up constructing the new result if the key was removed.
  • Complicated: Find the path to the key in one pass down, alterF style, then delete only if the key was present.

"Simple" should be faster in case the key is present, "Complicated" should be faster in case the key is absent. I think the simple way should be fine. Either way, unlike updateLookupWithKey, we don't construct a map/set if the key is absent.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions