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?
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.
I'd even suggest to add a generalized update operation that allows to handle the following use-cases:
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
Looks reasonable to me. Anyone want to have a go at implementing it? :)
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
(Just v, r) -> do
HT.insert ht k v
@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).
+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 ())
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!
@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:
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.