Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Please add a method to update a value in place #6

Open
peti opened this Issue · 8 comments

6 participants

@peti

I would like to update a value stored in a hashtable. The unordered-containers package provides methods like insertWith to do this, but the hashtable package unfortunately doesn't. The only way to update a value in place seems to be to do a lookup followed by an insert, but this strikes me as unnecessarily inefficient. Or am I missing something terribly obvious?

@fatlazycat

Had the same thought, looking for an insertWith. Thanks.

As a driver for this, my code with insertWith using HashMap is barely any different in performance than using the mutable version here.

@hvr

I'd even suggest to add a generalized update operation that allows to handle the following use-cases:

  • a way to communicate to the caller whether an entry existed for the key
  • to allow conditional updates (i.e. update only if the the key existed)
  • a way to communicate the previously stored value to the caller
  • conditionally delete an entry

This would lead to a type-signature of the following style:

updateEntry :: (Eq k, Hashable k) => h s k v -> k -> (Maybe v -> (Maybe v,a)) -> ST s a

where the Maybe v would encode the existence of the entry before and after the update.

PS: This is inspired by the Data.Map.alter operation

@gregorycollins

Looks reasonable to me. Anyone want to have a go at implementing it? :)

@hvr

Here's a more concrete pseudo-implementation to demonstrate the semantics I had in mind:

updateEntry ht k op = do
    mv <- HT.lookup ht k

    case op mv of
        (Nothing, r) -> do
            unless (isNothing mv) $
                HT.delete ht k
            return r

        (Just v, r) -> do
            HT.insert ht k v
            return r
@gregorycollins

@hvr: yes, semantically this would be ok but it shouldn't be implemented this way. We should lookup the slot and do the modify/delete in place (delete or insert would do the lookup twice).

@ppetr

+1, I'm missing a way how to get the old value overwritten by insert without an additional lookup. The suggested updateEntry sounds like an ideal solution.

Or perhaps a lens-like generalization:

updateEntry :: (Eq k, Hashable k, Traversable f)
            => h s k v -> k -> (Maybe v -> f (Maybe v)) -> ST s (f ())
@FranklinChen

It would be really great to have an update-in-place. I have verified that when I changed my code using immutable hashtables (from unordered-containers) to use this mutable API, having to look up and then reinsert basically doubled the time, and so I went back to the immutable for performance!

@gregorycollins

@ppetr re: traversable, in a library like this we're going to stick with the monomorphic :)

I also propose "mutate" as the function name. "alter" would work also.
Enough people want this, but hashtables is number 3 in my project queue (at best) right now. I don't have time to work on this issue but here is broadly what is needed, in this order:

  • add mutate to the typeclass
  • write failing unit tests for mutate
  • implement mutate for the three hashtable implementations, tests go green (and coverage 100%). Separate CLs would be preferred for each (and easier to review).

I've done the first two items (adding mutate to typeclass and writing a failing unit test), but I don't have time to work on it further right now. If you want to send pull requests please send them to the "mutate" branch I just uploaded to github.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.