ekmett/bytes — pull request #20 by sw17ch (2014-08-08 11:14:54+00:00)
Title: ExceptT instances for both MonadGet and MonadPut

This patch adds ExceptT instances (from what I can tell, ExcepT is the successor to ErrorT) to `Data.Bytes`.

## Notes

- It appears that ExceptT was added in `transformers >= 0.4`
- There also may be a reliance on `mtl >= 2.1`

## Testing

I, being a little unsure of my Monad Transformers skill level opted to build a small file to test my implementation. The body is as follows:

```haskell
module Main (main) where

import Data.Bytes.Put
import Control.Monad.Except
import Control.Monad.Trans.Except
import Data.Word
import qualified Data.ByteString as B

import qualified Data.Serialize.Put as S
import qualified Data.Serialize.Get as S

s :: B.ByteString
s = B.pack [100,200]

p :: S.PutM (Either String ()) -> IO ()
p a = case S.runPutM a of
(Left e, r) -> putStrLn $ "Failure: " ++ e ++ " (" ++ show r ++ ")"
(Right v, r) -> putStrLn $ "Success: " ++ show r ++ " (" ++ show v ++ ")"

g :: Show a => B.ByteString -> S.Get (Either String a) -> IO ()
g b a = print $ S.runGet a b

main :: IO ()
main = do
putStrLn "Pack a 10."
p $ runExceptT $ lift $ putWord8 10
putStrLn ""

putStrLn "Pack a 10 then fail."
p $ runExceptT $ (lift $ putWord8 10) >> throwE "ugh"
putStrLn ""

putStrLn "lookAheadM and fail then lookAheadM and succeed"
g s $ runExceptT $ do
_ <- lift $ S.lookAheadM (return Nothing :: S.Get (Maybe Word8))
lift $ S.lookAheadM (liftM Just S.getWord8 :: S.Get (Maybe Word8))
putStrLn ""

putStrLn "lookAheadE and fail then lookAheadM and succeed"
g s $ runExceptT $ do
e <- lift $ S.lookAheadE (return (Left "no") :: S.Get (Either String Word8))
w <- lift $ S.lookAheadE (liftM Right S.getWord8 :: S.Get (Either String Word8))
return (e,w)
putStrLn ""
```

The output of this simple program for me is as follows:

```
Pack a 10.
Success: "\n" (())

Pack a 10 then fail.
Failure: ugh ("\n")

lookAheadM and fail then lookAheadM and succeed
Right (Right (Just 100))

lookAheadE and fail then lookAheadM and succeed
Right (Right (Left "no",Right 100))
```

The extra `Either` wrappers in the `lookAheadM` and `lookAheadE` examples are artifacts of [`Data.Serialize.Get.Get`](https://hackage.haskell.org/package/cereal-0.4.0.1/docs/Data-Serialize-Get.html#t:Get).

I hope I've implemented this correctly. Thanks for your earlier guidance that sent me down this path.