Proposal: Non-allocating way to iterate over a Data.Map: traverseWithKey_
Roman Cheplyaka
roma at ro-che.info
Thu Jul 4 18:41:20 CEST 2013
* Henning Thielemann <lemming at henning-thielemann.de> [2013-07-02 21:57:58+0200]
>
> On Tue, 2 Jul 2013, Ryan Newton wrote:
>
> >Hi all,
> >Thanks for the responses. I want to go through and make sure I understand these.
> >
> >--------------------------------------------------------
> >First, Henning, won't both of these allocate in proportion to the size of the map?
> >
> > Map.foldrWithKey (\k a -> f k a >>) (return ())
> > Foldable.sequence_ . Map.mapWithKey f
> >
> >In particular, will the compiler be able to avoid allocating when building up that large monadic computation
> >in the foldrWithKey?
>
> Since it is a foldr, the first action can be run without knowing the
> following ones. That is, at no time all actions must be allocated.
It's not a foldr you would expect.
Here's the code:
foldrWithKey :: (k -> a -> b -> b) -> b -> Map k a -> b
foldrWithKey f z = go z
where
go z' Tip = z'
go z' (Bin _ kx x l r) = go (f kx x (go z' r)) l
You can see that 'go' is (partially) driven by the tree structure.
A more foldr-y foldr would look like
foldrWithKey :: (k -> a -> b -> b) -> b -> Map k a -> b
foldrWithKey f z = go z
where
go z' Tip = z'
go z' (Bin _ kx x l r) = f kx x (go (go z' r) l)
Perhaps this should be fixed? (Note that I haven't done any testing.)
Another note: I guess "non-allocating" in the title means that we're not
allocating anything of the order of map size. Some stack-like allocation
is inevitable since we're working with a tree, but it will be
logarithmic in the map size.
But then, it seems to me that the current version of foldrWithKey
should satsify that criterion, only with a big(ger) constant factor.
It always traverses the left branch accumulating z, then yields control
to f.
Roman
More information about the Libraries
mailing list