Skip to content

Commit 375d76b

Browse files
committed
Add Monad instance for Codec'
1 parent 2cd2c1b commit 375d76b

File tree

5 files changed

+50
-36
lines changed

5 files changed

+50
-36
lines changed

Data/Aeson/Codec.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ type ObjectBuilder = Const (Endo [ Pair ])
3333
type ObjectCodec a = Codec ObjectParser ObjectBuilder a
3434

3535
-- | Produce a key-value pair.
36-
pair :: ToJSON a => T.Text -> a -> ObjectBuilder ()
36+
pair :: ToJSON a => T.Text -> a -> ObjectBuilder b
3737
pair key val = Const $ Endo ((key .= val):)
3838

3939
-- | Read\/write a given value from/to a given key in the current object, using a given sub-codec.

Data/Binary/Bits/Codec.hs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,29 @@ import Control.Applicative
1010
import qualified Data.Binary.Bits.Get as G
1111
import Data.Binary.Bits.Put
1212
import qualified Data.Binary.Codec as B
13+
import Data.Functor ((<$))
1314

1415
import Data.Codec
1516
import Data.Word
1617

1718
type BitCodec a = Codec G.Block BitPut a
1819

1920
bool :: BitCodec Bool
20-
bool = Codec G.bool putBool
21+
bool = codec G.bool putBool
2122

2223
word8 :: Int -> BitCodec Word8
23-
word8 = Codec <$> G.word8 <*> putWord8
24+
word8 = codec <$> G.word8 <*> putWord8
2425

2526
word16be :: Int -> BitCodec Word16
26-
word16be = Codec <$> G.word16be <*> putWord16be
27+
word16be = codec <$> G.word16be <*> putWord16be
2728

2829
word32be :: Int -> BitCodec Word32
29-
word32be = Codec <$> G.word32be <*> putWord32be
30+
word32be = codec <$> G.word32be <*> putWord32be
3031

3132
word64be :: Int -> BitCodec Word64
32-
word64be = Codec <$> G.word64be <*> putWord64be
33+
word64be = codec <$> G.word64be <*> putWord64be
3334

3435
-- | Convert a `BitCodec` into a `B.BinaryCodec`.
3536
toBytes :: BitCodec a -> B.BinaryCodec a
3637
toBytes (Codec r w)
37-
= Codec (G.runBitGet $ G.block r) (runBitPut . w)
38+
= codec (G.runBitGet $ G.block r) (runBitPut . (() <$) . w)

Data/Binary/Codec.hs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,47 +27,47 @@ byteString :: Int -> BinaryCodec BS.ByteString
2727
byteString n = Codec
2828
{ parse = getByteString n
2929
, produce = \bs -> if BS.length bs == n
30-
then putByteString bs
30+
then putByteString bs >> return bs
3131
else fail "ByteString wrong size for field."
3232
}
3333

3434
word8 :: BinaryCodec Word8
35-
word8 = Codec getWord8 putWord8
35+
word8 = codec getWord8 putWord8
3636

3737
word16be :: BinaryCodec Word16
38-
word16be = Codec getWord16be putWord16be
38+
word16be = codec getWord16be putWord16be
3939

4040
word16le :: BinaryCodec Word16
41-
word16le = Codec getWord16le putWord16le
41+
word16le = codec getWord16le putWord16le
4242

4343
word16host :: BinaryCodec Word16
44-
word16host = Codec getWord16host putWord16host
44+
word16host = codec getWord16host putWord16host
4545

4646
word32be :: BinaryCodec Word32
47-
word32be = Codec getWord32be putWord32be
47+
word32be = codec getWord32be putWord32be
4848

4949
word32le :: BinaryCodec Word32
50-
word32le = Codec getWord32le putWord32le
50+
word32le = codec getWord32le putWord32le
5151

5252
word32host :: BinaryCodec Word32
53-
word32host = Codec getWord32host putWord32host
53+
word32host = codec getWord32host putWord32host
5454

5555
word64be :: BinaryCodec Word64
56-
word64be = Codec getWord64be putWord64be
56+
word64be = codec getWord64be putWord64be
5757

5858
word64le :: BinaryCodec Word64
59-
word64le = Codec getWord64le putWord64le
59+
word64le = codec getWord64le putWord64le
6060

6161
word64host :: BinaryCodec Word64
62-
word64host = Codec getWord64host putWord64host
62+
word64host = codec getWord64host putWord64host
6363

6464
wordhost :: BinaryCodec Word
65-
wordhost = Codec getWordhost putWordhost
65+
wordhost = codec getWordhost putWordhost
6666

6767
-- | Convert a `BinaryCodec` into a `ConcreteCodec` on lazy `LBS.ByteString`s.
6868
toLazyByteString :: BinaryCodec a -> ConcreteCodec LBS.ByteString (Either String) a
6969
toLazyByteString (Codec r w) = concrete
7070
(\bs -> case runGetOrFail r bs of
7171
Left ( _ , _, err ) -> Left err
7272
Right ( _, _, x ) -> Right x)
73-
(runPut . w)
73+
(runPut . (>> return ()) . w)

Data/Codec/Codec.hs

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module Data.Codec.Codec
22
( -- * Codecs
33
Codec'(..), Codec
4+
, codec
45
, (>-<)
56
-- * Concrete codecs
67
, ConcreteCodec, concrete, parseVal, produceVal
@@ -18,14 +19,16 @@ import Control.Applicative
1819
import Control.Monad ((>=>))
1920
import Control.Monad.Reader (ReaderT(..))
2021
import Data.Codec.Field
22+
import Data.Functor ((<$))
2123
import Data.Functor.Compose
2224
import Data.Maybe (fromMaybe)
25+
import Data.Traversable (traverse)
2326

24-
-- | De/serializer for the given types. Usually w ~ r, but they are separate
27+
-- | De/serializer for the given types. Usually `w ~ r`, but they are separate
2528
-- to allow for an `Applicative` instance.
2629
data Codec' fr fw w r = Codec
2730
{ parse :: fr r
28-
, produce :: w -> fw ()
31+
, produce :: w -> fw r
2932
}
3033
deriving Functor
3134

@@ -34,12 +37,21 @@ type Codec fr fw a = Codec' fr fw a a
3437

3538
-- Build up a serializer in parallel to a deserializer.
3639
instance (Applicative fw, Applicative fr) => Applicative (Codec' fr fw w) where
37-
pure x = Codec (pure x) (const $ pure ())
40+
pure x = Codec (pure x) (const $ pure x)
3841
Codec f fw <*> Codec x xw
39-
= Codec (f <*> x) (\w -> fw w *> xw w)
42+
= Codec (f <*> x) (\w -> fw w <*> xw w)
43+
44+
instance (Monad fw, Monad fr) => Monad (Codec' fr fw w) where
45+
return x = Codec (return x) (const $ return x)
46+
Codec a aw >>= f
47+
= Codec (a >>= parse . f) (\w -> aw w >>= \a -> produce (f a) w)
48+
49+
-- | Constructor of basic codecs.
50+
codec :: Functor fw => fr r -> (r -> fw ()) -> Codec fr fw r
51+
codec parse produce = Codec parse (\r -> r <$ produce r)
4052

4153
-- | Associate a `Field` with a `Codec` to create a `Codec` `Build`.
42-
(>-<) :: Functor fr => Field r a x y -> Codec fr fw a -> Build r (Codec' fr fw r) x y
54+
(>-<) :: (Functor fr, Functor fw) => Field r a x y -> Codec fr fw a -> Build r (Codec' fr fw r) x y
4355
Field c g >-< Codec r w
4456
= Build (c <$> Codec r (w . g))
4557

@@ -48,29 +60,29 @@ Field c g >-< Codec r w
4860
-- | Given a `Codec` for @a@, make one for `Maybe` @a@ that applies its deserializer optionally
4961
-- and does nothing when serializing `Nothing`.
5062
opt :: (Alternative fr, Applicative fw) => Codec fr fw a -> Codec fr fw (Maybe a)
51-
opt (Codec r w) = Codec (optional r) (maybe (pure ()) w)
63+
opt (Codec r w) = Codec (optional r) (traverse w)
5264

5365
-- | Turn a @`Codec` a@ into a @`Codec` b@ by providing an isomorphism.
54-
mapCodec :: Functor fr => (a -> b) -> (b -> a) -> Codec fr fw a -> Codec fr fw b
66+
mapCodec :: (Functor fr, Functor fw) => (a -> b) -> (b -> a) -> Codec fr fw a -> Codec fr fw b
5567
mapCodec = mapCodec'
5668

5769
-- | Map a field codec monadically. Useful for error handling but care must be taken to make sure that
5870
-- the results are still complementary.
5971
mapCodecM :: (Monad fr, Monad fw) => (a -> fr b) -> (b -> fw a) -> Codec fr fw a -> Codec fr fw b
6072
mapCodecM to from (Codec r w)
61-
= Codec (r >>= to) (from >=> w)
73+
= Codec (r >>= to) (\b -> from b >>= w >> return b)
6274

6375
-- | Map the contexts of a given `Codec`.
64-
mapCodecF :: (fr a -> gr a) -> (fw () -> gw ()) -> Codec fr fw a -> Codec gr gw a
76+
mapCodecF :: (fr a -> gr a) -> (fw a -> gw a) -> Codec fr fw a -> Codec gr gw a
6577
mapCodecF fr fw (Codec r w)
6678
= Codec (fr r) (fw . w)
6779

6880
-- | Independently map the two components of a `Codec'`.
6981
--
7082
-- Generalizes `mapCodec`.
71-
mapCodec' :: Functor fr => (a -> b) -> (c -> d) -> Codec' fr fw d a -> Codec' fr fw c b
83+
mapCodec' :: (Functor fr, Functor fw) => (a -> b) -> (c -> d) -> Codec' fr fw d a -> Codec' fr fw c b
7284
mapCodec' to from (Codec r w)
73-
= Codec (to <$> r) (w . from)
85+
= fmap to $ Codec r (w . from)
7486

7587
-- | Map on the `produce` component of a `Codec`.
7688
--
@@ -113,7 +125,7 @@ type PartialCodec fr fw a = Codec fr (Compose Maybe fw) a
113125
-- | Finish a codec construction with a @`Con` r@ to produce a `PartialCodec`.
114126
-- This will check that the given record has the appropriate constructor
115127
-- before serializing.
116-
cbuild :: (Functor fr, Buildable r y)
128+
cbuild :: (Functor fr, Functor fw, Buildable r y)
117129
=> Con r x -> Build r (Codec' fr fw r) x y -> PartialCodec fr fw r
118130
cbuild (Con c p) = assume p . build c
119131

@@ -136,6 +148,6 @@ cd <-> acd = Codec
136148
}
137149

138150
-- | Attempt to get a serialization for a given value.
139-
produceMaybe :: PartialCodec fr fw a -> a -> Maybe (fw ())
151+
produceMaybe :: PartialCodec fr fw a -> a -> Maybe (fw a)
140152
produceMaybe (Codec _ w) x
141153
= getCompose (w x)

Foreign/Codec.hs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module Foreign.Codec
77
) where
88

99
import Control.Monad.Reader
10+
import Data.Functor ((<$))
1011
import Foreign
1112

1213
import Data.Codec.Codec
@@ -26,7 +27,7 @@ peekWith (Codec r _)
2627
-- | Poke a value using a `ForeignCodec'`.
2728
pokeWith :: ForeignCodec' p a -> Ptr p -> a -> IO ()
2829
pokeWith (Codec _ w) ptr x
29-
= runReaderT (w x) ptr
30+
= runReaderT (() <$ w x) ptr
3031

3132
-- | A codec for a field of a foreign structure, given its byte offset and a sub-codec.
3233
-- You can get an offset easily using @{#offset struct_type, field}@ with @hsc2hs@.
@@ -38,7 +39,7 @@ field off cd = Codec
3839

3940
-- | A `ForeignCodec` for any `Storable` type.
4041
storable :: Storable a => ForeignCodec a
41-
storable = Codec (ReaderT peek) (\x -> ReaderT (`poke`x))
42+
storable = codec (ReaderT peek) (\x -> ReaderT (`poke`x))
4243

4344
castContext :: ForeignCodec' c a -> ForeignCodec' c' a
4445
castContext = mapCodecF castc castc
@@ -54,4 +55,4 @@ cBool = castContext storable
5455

5556
-- | Restrict the pointer type of a given codec. Utility function for the @numField@ macro.
5657
codecFor :: c -> ForeignCodec' c a -> ForeignCodec' c a
57-
codecFor _ = id
58+
codecFor _ = id

0 commit comments

Comments
 (0)