Search code examples
haskellhaskell-streaming

Missing type of result? In function length :: Monad m => ByteString m r -> m (Of Int r)


I have a simple function that reads a binary file one byte at a time. It get a compile time error, below. The problem seems to be that bs2, the resulting ByteString of BSSC.length, has an unknown type. Am I missing a type constraint on r?

import qualified Data.ByteString.Streaming.Char8 as BSSC

main :: IO ()
main = runResourceT $ dump $ BSSC.readFile "filename"

dump :: (MonadIO m) => BSSC.ByteString m r -> m ()                                                                                                                                                
dump bs = do
    len :> bs2 <- BSSC.length bs          -- Data.Functor.Of (:>)
    if len <= 1 then return ()
    else dump $ BSSC.putStr $ BSSC.splitAt 1 bs2

Compile-time error:

Main.hs:166:46: error:
  • Couldn't match expected type ‘BSSC.ByteString
                                    (BSSC.ByteString m) r0’
                with actual type ‘r’
    ‘r’ is a rigid type variable bound by
      the type signature for:
        dump :: forall (m :: * -> *) r.
                MonadIO m =>
                BSSC.ByteString m r -> m ()
      at Main.hs:162:9
  • In the second argument of ‘BSSC.splitAt’, namely ‘bs2’
    In the second argument of ‘($)’, namely ‘BSSC.splitAt 1 bs2’
    In the second argument of ‘($)’, namely
      ‘BSSC.putStr $ BSSC.splitAt 1 bs2’
  • Relevant bindings include
      bs2 :: r (bound at Main.hs:164:12)
      bs :: BSSC.ByteString m r (bound at Main.hs:163:6)
      dump :: BSSC.ByteString m r -> m () (bound at Main.hs:163:1)

Solution

  • The ByteString type from streaming-bytestring has two type parameters. The first is the base monad m in which the values are produced (typically IO).

    The second is a special ending value r that is returned once the ByteString is exhausted. Usually it will be an uninformative (). However, it becomes very useful for defining functions like splitAt :: Monad m => Int64 -> ByteString m r -> ByteString m (ByteString m r). The type means: "give me a limit position and a effectful stream of bytes that returns with an r, I will give you another stream that is no longer than the limit and returns with a effectful stream of bytes that returns with an r." This ending stream nested within the outer one can only be reached after the outer one is exhausted.

    length has type Monad m => ByteString m r -> m (Of Int r). It consumes the stream of values it receives as argument and returns an action in the base monad. The ByteString exists no more. The bs2 that you pass to splitAt is not a ByteString but the ending value of the original ByteString, which has type r. And this causes a type error.