1

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)
danidiaz
  • 26,936
  • 4
  • 45
  • 95
paperduck
  • 1,175
  • 1
  • 16
  • 26
  • Your problem that in `len :> bs2 <- BSSC.length bs` `bs2` is not content (not even `ByteString`), but just `()`. If you want to operate with `ByteString` it is looks like you can't extract value out of it, use `if` and continue to use `ByteString`. – talex May 14 '19 at 10:18
  • If you want to recursive process your string you have to use `uncons` to get head and tail, and do recursive call with tail. – talex May 14 '19 at 10:21

2 Answers2

1

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.

danidiaz
  • 26,936
  • 4
  • 45
  • 95
0

You can hotfix type mismatch with using bs as second parameter to splitAt

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
    if len <= 1 then return ()
    else dump $ BSSC.putStr $ BSSC.splitAt 1 bs

but it won't work as you expect. I guess it will read file as many times as many letter in it.

If you want recursion you should go with

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
    split <- BSSC.uncons bs
    case split of
       Left _        -> return ()
       Right (x, xs) -> putStr (show x) >> dump xs

I don't have compiler with me, so it is possibly there are compilation prblems in my snippets.

talex
  • 17,973
  • 3
  • 29
  • 66