Andrew Coppin wrote:
After many hours of effort, I came up with these:

 data Writer x
 instance Monad Writer
 run_writer :: Writer () -> ByteString
 write1 :: Bool -> Writer ()
 write8 :: Word8 -> Writer ()
 write16 :: Word16 -> Writer ()
 write32 :: Word32 -> Writer ()
 write64 :: Word64 -> Writer ()
 writeN :: Word8 -> Word32 -> Writer ()

 data Reader x
 instance Monad Reader
 run_reader :: Reader x -> ByteString -> x
 is_EOF :: Reader Bool
 read1 :: Reader Bool
 read8 :: Reader Word8
 read16 :: Reader Word16
 read32 :: Reader Word32
 read64 :: Reader Word64
 readN :: Word8 -> Reader Word32

How would you write QuickCheck properties for these?

For starters, what would be a good set of properties to confirm that any monad is actually working correctly? More particularly, how about a state monad? It's easy to screw up the implementation and pass the wrong state around. How would you catch that?

Secondly, the monads themselves. I started writing things like "if X has the lowest bit set then the lowest bit of the final byte of the output should be set"... but then all I'm really doing is reimplementing the algorithm as a property rather than a monad! If a property fails, is the program wrong or is the property wrong?

In the end, what I opted to do was define various properties where I take some arbitrary data, write it with the Writer monad, then read it back with the Reader monad and confirm that the data stays identical. (This actually fails for writeN, which writes the N least-significant bits of the supplied data, so you need to apply some masking before doing equity. Or, equivilently, reject some test values...)

Looking at the QuickCheck paper, it seems I should probably have done some checking that the size of the output is correct. I didn't actually bother because it's really easy to get right, whereas strickiness with bit-shifts and indexing is all too easy to screw up.

What I finally did was try writing/reading with each primitive (to check that actually works properly), and then again with a random number of individual bits packed on each side to give random alignment (to check that the index adjustments actually work right). It's easy to make the code work correctly with a certain alignment, but fail spectacularly otherwise. It's packed at *both* ends because it's also quite easy to make it write out the correct bit pattern, but leave the bit pointer with the wrong value, causing subsequent writes to screw up.

How would you approach this one? All hints welcomed.

_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to