[Haskell-cafe] Re: QuickCheck [Architecturally flawed]

Andrew Coppin andrewcoppin at btinternet.com
Fri Jul 11 14:10:54 EDT 2008

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.

More information about the Haskell-Cafe mailing list