
[redirecting to libraries] On Mon, Feb 04, 2008 at 01:09:00PM +0100, Stephan Friedrichs wrote:
apfelmus@quantentunnel.de wrote:
[newtype Ord a => Reverse a = Reverse { unReverse :: a }]
Yes. I mean "non-standard" in the software-reuse sense, i.e. Ord is for user-defined orderings and should be the only such mechanism in order to enable reuse. In fact, Data.Heap clearly shows that Data.Ord is currently missing functionality.
Ah, now I see. The entire ordering policy mechanism - no matter how it is going to be solved - belongs into Data.Ord and not in Data.Heap. As soon as Data.Ord provides a solution, I'll use it in Data.Heap.
Unfortunately the Data.Ord module is constrained to use only Haskell 98 features, because it is used by all implementations. So it could include the above Reverse (also mentioned in the group-by HW paper), but not the various OrdPolicy proposals. They could go in a separate module, though. Off on a tangent: the containers package, which collects portable implementations of general purpose data structures, could use a good priority queue implementation. However it is also constrained to use only Haskell 98 features, which would rule out using policies. Also, using weight-biased leftist trees (Cho & Sahni, 1996) instead of Knuth's rank-biased leftist trees would offer O(1) size for free. And they come out slightly faster in my test runs.

Ross Paterson wrote:
Stephan Friedrichs wrote:
apfelmus wrote:
[newtype Ord a => Reverse a = Reverse { unReverse :: a }]
Yes. I mean "non-standard" in the software-reuse sense, i.e. Ord is for user-defined orderings and should be the only such mechanism in order to enable reuse. In fact, Data.Heap clearly shows that Data.Ord is currently missing functionality. Ah, now I see. The entire ordering policy mechanism - no matter how it is going to be solved - belongs into Data.Ord and not in Data.Heap. As soon as Data.Ord provides a solution, I'll use it in Data.Heap.
Unfortunately the Data.Ord module is constrained to use only Haskell 98 features, because it is used by all implementations. So it could include the above Reverse (also mentioned in the group-by HW paper), but not the various OrdPolicy proposals.
The advantage of policies is that the Heap interface is implemented exactly once. Using Reverse is no problem, but you'd have to implement one normal version (MinHeap a) and one boilerplate version (MinHeap (Reverse a)). Implementing Heap p a for both p = Identity and p = Reverse at once can eliminate this boilerplate. Fortunately, the "polymorphic policies" Identity and Reverse can be implemented in Haskell98! data OrdBy p a = OrdBy { unOrdBy :: a } class OrdPolicy p where compare' :: Ord a => OrdBy p a -> OrdBy p a -> Ordering instance (Ord a, OrdPolicy p) => Ord (OrdBy p a) where compare = compare' class Heap h where empty :: Ord a => h a insert :: Ord a => a -> h a -> h a viewHead :: Ord a => h a -> Maybe (a, h a) data HeapP p a = ... -- MinHeap (OrdBy p a) instance OrdPolicy p => Heap (HeapP p) where ... What probably can't be done in Haskell98 are policies p that only work for specific types, i.e. such that OrdBy p a is not always an instance of Ord . Of course, there is the question whether they are needed or whether they are just overengineering. I hope that Stephan Friedrichs has a compelling example for the former? :) That being said, I think there might be a Haskell98 way to implement even them. The trick here is that while class contexts like Ord (OrdBy p a) are not H98, contexts like Ord (p a) with type variables only are. class Identity f where to :: a -> f a from :: f a -> a -- subject to to . from = id and from . to = id -- the latter holds automatically thanks to parametricity data Heap p a = ... -- MinHeap (p a) empty :: (Identity p, Ord (p a)) => Heap p a insert :: (Identity p, Ord (p a)) => a -> Heap p a -> Heap p a viewHead :: (Identity p, Ord (p a)) => Heap p a -> Maybe (a, Heap p a) In other words, the context (Identity p, Ord (p a)) expresses what a multi-parameter type class OrdPolicy p a would do, too. I wonder whether this trick can be used to encode arbitrary multi-parameter type classes in plain Haskell98. I guess not, because interestingly, Ord (p a) is not a valid context for class or instance declarations, it's only ok for function definitions. So, defining stuff like instance Show (p a) => Show (Heap p a) where ... is not possible, which limits this approach somewhat. Another limit of non-polymorphic policies is that the Heap class would have to be multi-parameter (which it should be anyway of course, but that's not H98). Regards, apfelmus
participants (2)
-
apfelmus
-
Ross Paterson