In the following:import Data.Bits (popCount, shiftL, (.|.))import Data.Char (chr, ord)import Data.Word (Word8)import qualified Data.ByteString as Bdata MByteSequence = MByteSequence [Word8] -- a sequence of map bytes (len 0-15)data Atom = TerminationAtom |            NonOffsetAtom { nonOffsetGapLength :: Integer -- the number of gap bytes (max 2**53-1)                          , nonOffsetGapValue :: Bool -- the value of the gap bytes                          , followingMapBytes :: MByteSequence -- a sequence of following map bytes (len 1-15)                          } |            OffsetAtom { offsetGapLength :: Integer -- the number of gap bytes (max 2**53-1)                       , offsetGapValue :: Bool -- the value of the gap bytes and the majority of the offset byte                       , offsetBit :: Int -- the bit with a different value in the offset byte (0-7)                       }getAtomType :: Atom -> IntgetAtomType TerminationAtom = 1getAtomType (NonOffsetAtom len _ _)  | len <= 3 = fromInteger len  | len > 3  = 4getAtomType (OffsetAtom len val _)  | len <= 3 && val == False = 5  | len > 3                  = 6  | len <= 3 && val == True  = 7getFillField :: Atom -> IntgetFillField TerminationAtom = 0getFillField (NonOffsetAtom _ val _) = if val then 1 else 0getFillField (OffsetAtom len val _) = if len > 3 then (if val then 1 else 0) else fromInteger lengetDataField :: Atom -> IntgetDataField TerminationAtom = 0getDataField (NonOffsetAtom _ val (MByteSequence mbytes))  | length mbytes == 1 &&    head mbytes == (if val then 0 else 0xFF) = 0  | otherwise                                = length mbytesgetDataField (OffsetAtom _ _ bit) = bitserialize :: Atom -> Word8serialize TerminationAtom = 0serialize (NonOffsetAtom x y z) = fromIntegral $ (getAtomType all) `shiftL` 6 .|.                                                 (getFillField all) `shiftL` 4 .|.                                                 (getDataField all)  where all = NonOffsetAtom x y zserialize (OffsetAtom x y z) = fromIntegral $ (getAtomType all) `shiftL` 6 .|.                                              (getFillField all) `shiftL` 3 .|.                                              (getDataField all)  where all = OffsetAtom x y zTo me, serialize especially seems pretty ugly; the use of the where clauses and the similarity of the latter two pattern match cases all seems bad to me, yet I'm unsure what should be done instead. The other functions, while split up into logical blocks, seem perhaps too small? Should I use Word8 as the return type of all the functions (the return values are all small enough to fit)?I'd welcome any feedback on code style, Haskell best practices (in general!), and any design patterns I should be using here!
Separation of code into functions and use of patern matching in Haskell
haskell;serialization