I asked this earlier on #haskell, but it was quiet hour.  Here goes.

Suppose I have an abstract data type for prefix dictionaries of bytestrings. I define a typeclass in a module PrefixDict.Class:

   module PrefixDict.Class where

   import qualified Data.ByteString.Char8 as B

   type Word = B.ByteString

   class PrefixDict d where
       empty :: d
       isEmpty :: d -> Bool
       insert :: Word -> d -> d
       delete :: d -> Word -> d
       hasWord :: d -> Word -> Bool
       hasPrefix :: d -> Word -> Bool

I then write several implementations, including a naive list implementation, and a trie implementation, in the modules PrefixDict.ListDict and PrefixDict.TrieDict, both looking something like this:

   module PrefixDict.ListDict where

   import PrefixDict.Class

   data ListDict = ListDict [Word]

   instance PrefixDict ListDict where
       ...

Now, there are many properties that should be satisfied by any implementation of PrefixDict, that are not easily captured in the type system. Trying to be a good coder, I write up some generic QuickCheck properties, such as

   prop_insert_idempotent :: (Eq d, PrefixDict d) => d -> Word -> Bool
   prop_insert_idempotent d w = insert w d' == d'
     where d' = insert w d

The problem is this: I can't think of a non-kludged way of checking that TrieDict and ListDict satisfy this abstract property without having to add explicit type signatures, e.g.

   quickCheck (prop_insert_idempotent :: TrieDict -> Word -> Bool)

I have many abstract properties, and I *really* don't like having to put an explicit signature on each one.

I came up with a hackishway that minimizes the number of signatures required, by checking all abstract properties in one, generic function, reflecting a type as a value, and using scoped type variables:

   testAbstractProperties :: forall d. (PrefixDict d) => d -> IO ()
   testAbstractProperties _ = do
     quickCheck (prop_insert_idempotent :: d -> Word -> Bool)
     quickCheck (prop_delete_works :: d -> Word -> Property)
     ...

I then call this function from a "test suite" function written for each module, e.g.

   testTrieDict :: IO ()
   testTrieDict = do
     testAbstractProperties (undefined :: TrieDict)
     testTrieDictSpecificProperties

This means I only have to write the type signatures on the properties in one place, but it is still less than ideal.

Is there a better way of doing this, or is Haskell simply not good at this kind of data abstraction?

Best,
Brad Larsen
_______________________________________________
Haskell-Cafe mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to