-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Movable for amortized structures #445
Comments
I don't think that it's ever ok to make Ok, that's a little strong. It's not ok to make Why is that? Maybe you have a linear Now, if you keep the data structure fully strict, with no thunks inside, then you're probably good. I don't have good mathematical tools, to think through this yet, though. Always proceed with care 🙂 |
The structure I'm talking about holds only unrestricted values, so your concern about strictness isn't relevant. Thinking about it again, I believe |
It is though, because of the linked structure of the pairing heap, independently of the data held in the nodes.
I'm not sure what implementation of |
A pairing heap looks roughly like data PQ a where
Empty :: PQ a
Node :: !a -> [PQ a] -> PQ a with the invariants that the queues in the list are nonempty and the element in each node is no greater than the elements in its children. I'm suggesting something like fromAscList :: [Ur a] %1-> Ur (PQ a)
fromAscList [] = Empty
fromAscList (Ur a : as)
| Ur q <- fromAscList as
, not (null q)
= Ur (Node a [q])
| otherwise
= Ur (Node a [])
move q = fromAscList (toAscList q) The key thing is that each node has at most one child, so rebuilding when deleting the minimum is cheap. Cheaper than it needs to be, even, but I don't know how to take advantage of that. |
This implementation of move is unproblematic. You can implement it client-side too. You could also simply implement Is your question philosophical? Like “is it a good idea to have |
The unsafe coercion was a mistake. Making |
Aha! Very interesting. So the idea is, in an amortized structure, you do a bunch of cheap operations, but eventually you have to do an expensive operation. You certainly don't want to do the expensive operation many times, so you want an implementation of This is an alternative to Okasaki's style laziness, where you the expensive operation is shared/memoised. Probably easier to implement too. It also reminds me of this limitation of laziness I tweeted about last year. |
Yes, that's the idea. Okasaki-style structures (e.g., lazy-spined binomial queues as in the |
(well in their defence pairing heap are a very unfair data structure: they are dead simple, and perform well in many applications) Is there anything left on this issue, that you'd want to achieve? Or should we close? |
I think there may be room for some documentation somewhere? I don't know if that belongs here, though, or in the GHC docs. In case you're curious, this is the linear pairing heap implementation I hacked up. |
I think (I'm pretty sure in fact) I wrote a comment here, and never finished. It got lost. Sorry. I'm seeing this again now. What kind of documentation do you imagine? We can make it happen, I'm just not clear on what it could look like. |
In some cases, a pure amortized data structure may only be efficient when used in a single threaded fashion. This suggests a linear interface
Would it be appropriate for something like
PQ a
to be an instance ofMoveable
? If so,move
would be a no-op (move = Unsafe.coerce Ur
), and the user would be fully responsible for making sure performance was okay in context. Or would it be better to only have aperflyRiskyMove
in the module to make it clear that there's a performance risk?The
Dupable
instance is much more obviously reasonable: it would fully reassociate the queue into a "safe" configuration that has no credit. Do we want to provide a module giving an example of a structure like this?The text was updated successfully, but these errors were encountered: