Search code examples
haskellstate-monad

Monadic way to read bits from Get monad


I need to read some amount of bits from Get monad. Now my code looks like

readBits :: Int -> Int -> Get (Word32, Int)
readBits count state = ...

readValue :: Get (Word32, Word32)
readValue = do
   -- read fst bit count 
   (bits1, s0) <- readBits 5 0
   -- read bits1 bits as fst
   (fst, s1) <- readBits bits1 s0
   -- read snd bit count
   (bits2, s2) <- readBits 5 s1
   -- read bits2 bits as snd
   (snd, s3) <- readBits bits2 s2  
   -- flush incomplete byte
   when (s3 /= 0) $ skip 1
   return (fst, snd)

I want to wrap this into some kind of state monad, to have code like

readBits :: Int -> BitReader Word32
readBits count = ...

runBitReader :: BitReader a -> Get a

readValue :: Get (Word32, Word32) 
readValue = runBitReader $ do
      bits1 <- readBits 5
      fst <- readBits bits1
      bits2 <- readBits 5
      snd <- readBits bits2
      return (fst, snd)

What functions should I implement? How should they be implemented?

I have looked into Get and BitGet source code, but not fully understand what's going on.


Solution

  • This is the most typical usecase for Monad Transformers.

    You have defined most of the structure correctly. To answer your questions

    What functions should I implement? 
    
    • Well you first need to wrap Get monad into the StateT Transformer to get BitReader.
    • You need to implement proper definition for readBits using get to get the current state and put to save the state back.
    • You need to run your code wrapped in BitReader to get back the output in Get Monad. So you need to define runBitReader using runStateT.

    To answer your next question.

    How should they be implemented?
    

    I have given the possible implementation. You still need to define some functions to make it work.

    import Control.Monad.State 
    import qualified Control.Monad.State as ST
    import Data.Binary 
    
    type BitReader = StateT Int Get
    
    readBits' :: Int -> Int -> Get (Word32, Int)
    readBits' = undefined   
    
    readBits :: Int -> BitReader Word32
    readBits n = do 
        s0 <- ST.get 
        (a,s1) <- lift $ readBits' n s0
        ST.put s1 
        return a
    
    
    runBitReader :: BitReader a -> Get a
    runBitReader w = do 
        (a,s) <- runStateT w 0
        return a
    
    readValue = do
          fst <- readBits 5
          snd <- readBits 10
          return (fst, snd) 
    

    I don't know how looking into code of Get was going to help you. You were looking in the wrong house. You need to read about State Monads and Monad Transformers.

    You can read more about monad transformers here.