
Thanks again Daniel!! I've been looking at the Edison package, it has a big class hierarchy without default implementations using functional dependencies and I found tidier doing something similar with associated types. Writing default implementations continues to be troublesome. Here is an example adding an elems function to the Collection class elems :: c -> [Elem c] I can't write on the MultiMapClass class a default implementation for getValueList like this getValueList :: Key m -> m -> [Value m] getValueList k m = elems $ getValues k m because elems returns [Elem (Coll m)] so I have to write getValueList :: Key m -> m -> [Elem (Coll m)] and it doesn't matter that this means the same for the implementations I have Elem (Set.Set a) ~ Value (MultiMap k v) and Elem IntSet.IntSet ~ Value IntMultiMap Now when using classes like this you need to need to think twice when coding! Typing becomes much more complicated. I don't know if I am too object oriented or is the lack of IDEs but reusing code, grouping together code with similarities and managing many modules is not easy with Haskell. Things that I found essential to write large programs. I'm starting to think that the easiest way of writing generic and reusable code with Haskell is writing a Haskell parser and code generator in Haskell. On Fri, May 27, 2011 at 3:00 PM, Daniel Fischer < daniel.is.fischer@googlemail.com> wrote:
On Friday 27 May 2011 19:23:54, Federico Mastellone wrote:
Now I have a new problem, it's getting really difficult to program generically and to create highly parameterized libraries.
Yes.
So far so good with type families, but when I want to return a generic Set for the getValues function and provide a default implementation for getValuesCount function I don't know how to do it, I don't even know if it is possible.
You can't return a generic Set in getValues (unless you jump through a lot of hoops, IntMultiMap has IntSets, so you'd have to transform those to Sets, ... yuck) and default implementations wouldn't be possible [or at least rather horrible], but
newtype MultiMap k v = MultiMap (Map.Map k (Set.Set v))
newtype IntMultiMap = IntMultiMap (IntMap.IntMap IntSet.IntSet)
class (Ord (Elem c)) => Collection c where type Elem c emtpy :: c singleton :: Elem c -> c size :: c -> Int null :: c -> Bool member :: Elem c -> c -> Bool ...
class MultiMapClass m where
class (Collection (Coll m)) => MultiMapClass m where
type Key m type Value m
type Coll m
empty :: m addValue :: Key m -> Value m -> m -> m getValues :: Key m -> m -> Set (Value m)
getValues :: Key m -> m -> Coll m
getValueCount :: Key m -> m -> Int getValueCount k m = Set.size $ getValues k m
getValueCount k m = size (getValues k m)
Should work or be possible to make working. But things get more complicated the more generic functionality you want to add.
It would probably be possible to get a more elegant implementation if you designed the library to use a class-based interface from the ground up (take a look at the edison library [EdisonAPI and EdisonCore on hackage] for an idea of how to structure the classes - edison is old, it was created long before type families or functional dependencies were available, I don't know what new stuff was incorporated into it, I suspect not too much, so you could probably improve the design with the new powerful tools at your hands, but as a source of inspiration, it should still serve well).
The problem is that, as a rule of thumb, class based genericity costs performance. And the point of the containers package was to provide performant data structures, the genericity was wilfully forsaken.
So, perhaps writing a generic envelope for the containers types might not be the best option. It could be better to start from a clean sheet. [disclaimer: I have not thought about how to do that, nor looked at the API or implementation from that point of view, so my hunch may be quite wrong.]
Cheers, Daniel
-- Federico Mastellone Computer Science Engineer - ITBA ".. there are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult." Tony Hoare, 1980 ACM Turing Award Lecture.