packages feed

waargonaut 0.3.0.0 → 0.4.0.0

raw patch · 16 files changed

+843/−355 lines, 16 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Waargonaut.Encode: Encoder :: a -> f Json -> Encoder f a
- Waargonaut.Encode: generaliseEncoder' :: Monad f => Encoder' a -> Encoder f a
- Waargonaut.Encode: instance (Waargonaut.Encode.Encoder f a ~ t) => Control.Lens.Wrapped.Rewrapped (Waargonaut.Encode.Encoder f a) t
- Waargonaut.Encode: instance Control.Lens.Wrapped.Wrapped (Waargonaut.Encode.Encoder f a)
- Waargonaut.Encode: instance Control.Monad.Morph.MFunctor Waargonaut.Encode.Encoder
- Waargonaut.Encode: instance Data.Functor.Contravariant.Contravariant (Waargonaut.Encode.Encoder f)
- Waargonaut.Encode: newtype Encoder f a
+ Waargonaut.Encode: combineObjects :: Applicative f => (a -> (b, c)) -> ObjEncoder f b -> ObjEncoder f c -> ObjEncoder f a
+ Waargonaut.Encode: extendMapLikeObject :: Functor f => ObjEncoder f a -> a -> (MapLikeObj WS Json -> MapLikeObj WS Json) -> f Json
+ Waargonaut.Encode: extendObject :: Functor f => ObjEncoder f a -> a -> (JObject WS Json -> JObject WS Json) -> f Json
+ Waargonaut.Encode: generaliseEncoder :: Monad f => EncoderFns i Identity a -> EncoderFns i f a
+ Waargonaut.Encode: jsonEncoder :: (a -> f Json) -> EncoderFns Json f a
+ Waargonaut.Encode: objEncoder :: (a -> f (JObject WS Json)) -> EncoderFns (JObject WS Json) f a
+ Waargonaut.Encode: type Encoder f a = EncoderFns Json f a
+ Waargonaut.Encode: type ObjEncoder f a = EncoderFns (JObject WS Json) f a
+ Waargonaut.Encode: type ObjEncoder' a = EncoderFns (JObject WS Json) Identity a
+ Waargonaut.Encode.Types: EncoderFns :: i -> Json -> a -> f i -> EncoderFns i f a
+ Waargonaut.Encode.Types: [finaliseEncoding] :: EncoderFns i f a -> i -> Json
+ Waargonaut.Encode.Types: [initialEncoding] :: EncoderFns i f a -> a -> f i
+ Waargonaut.Encode.Types: data EncoderFns i f a
+ Waargonaut.Encode.Types: generaliseEncoder :: Monad f => EncoderFns i Identity a -> EncoderFns i f a
+ Waargonaut.Encode.Types: instance Control.Monad.Morph.MFunctor (Waargonaut.Encode.Types.EncoderFns i)
+ Waargonaut.Encode.Types: instance Data.Functor.Contravariant.Contravariant (Waargonaut.Encode.Types.EncoderFns o f)
+ Waargonaut.Encode.Types: instance GHC.Base.Applicative f => Data.Functor.Contravariant.Divisible.Decidable (Waargonaut.Encode.Types.EncoderFns (Waargonaut.Types.JObject.JObject Waargonaut.Types.Whitespace.WS Waargonaut.Types.Json.Json) f)
+ Waargonaut.Encode.Types: instance GHC.Base.Applicative f => Data.Functor.Contravariant.Divisible.Divisible (Waargonaut.Encode.Types.EncoderFns (Waargonaut.Types.JObject.JObject Waargonaut.Types.Whitespace.WS Waargonaut.Types.Json.Json) f)
+ Waargonaut.Encode.Types: jsonEncoder :: (a -> f Json) -> EncoderFns Json f a
+ Waargonaut.Encode.Types: objEncoder :: (a -> f (JObject WS Json)) -> EncoderFns (JObject WS Json) f a
+ Waargonaut.Encode.Types: runEncoder :: Functor f => EncoderFns i f a -> a -> f Json
+ Waargonaut.Encode.Types: runPureEncoder :: EncoderFns i Identity a -> a -> Json
+ Waargonaut.Encode.Types: type Encoder f a = EncoderFns Json f a
+ Waargonaut.Encode.Types: type Encoder' a = EncoderFns Json Identity a
+ Waargonaut.Encode.Types: type ObjEncoder f a = EncoderFns (JObject WS Json) f a
+ Waargonaut.Encode.Types: type ObjEncoder' a = EncoderFns (JObject WS Json) Identity a
+ Waargonaut.Types.JArray: instance Control.Lens.At.Ixed (Waargonaut.Types.JArray.JArray ws a)
+ Waargonaut.Types.JObject: _MapLikeObj :: (Semigroup ws, Monoid ws) => Prism' (JObject ws a) (MapLikeObj ws a)
+ Waargonaut.Types.Json: aix :: (AsJType r ws a, Semigroup ws, Monoid ws) => Int -> Traversal' r a
+ Waargonaut.Types.Json: oat :: (AsJType r ws a, Semigroup ws, Monoid ws) => Text -> Traversal' r (Maybe a)
+ Waargonaut.Types.Json: oix :: (Semigroup ws, Monoid ws, AsJType r ws a) => Int -> Traversal' r a
- Waargonaut.Encode: either :: Encoder f a -> Encoder f b -> Encoder f (Either a b)
+ Waargonaut.Encode: either :: Functor f => Encoder f a -> Encoder f b -> Encoder f (Either a b)
- Waargonaut.Encode: maybe :: Encoder f () -> Encoder f a -> Encoder f (Maybe a)
+ Waargonaut.Encode: maybe :: Functor f => Encoder f () -> Encoder f a -> Encoder f (Maybe a)
- Waargonaut.Encode: runEncoder :: Encoder f a -> a -> f Json
+ Waargonaut.Encode: runEncoder :: Functor f => EncoderFns i f a -> a -> f Json
- Waargonaut.Encode: runPureEncoder :: Encoder' a -> a -> Json
+ Waargonaut.Encode: runPureEncoder :: EncoderFns i Identity a -> a -> Json
- Waargonaut.Encode: simplePureEncodeNoSpaces :: Encoder' a -> a -> ByteString
+ Waargonaut.Encode: simplePureEncodeNoSpaces :: Encoder Identity a -> a -> ByteString
- Waargonaut.Encode: type Encoder' = Encoder Identity
+ Waargonaut.Encode: type Encoder' a = EncoderFns Json Identity a

Files

changelog.md view
@@ -1,5 +1,18 @@ # Revision history for waargonaut +## 0.4.0.0  -- 2018-11-19++* Redesign & rebuild of `Encoder` internals to allow for greater control and flexibility+* Factor our law tests into their own module (a recheck of these tests is needed)+* Fixed bug in `list` and `nonempty` decoders+* Fixed bug in `foldCursor` function+* Fixed bug in `Cons` instance for `CommaSep`+* Fixed bug in documentation for `atKey`+* Added `_MapLikeObj` `Prism`+* Added some optics into object / maplikeobj keys+* Fixed bug in `maybeOrNull` decoder to be more strict in what it accepts+* Rewrote `either` decoder in terms of the alternative instance to allow for better errors+ ## 0.3.0.0  -- 2018-11-14  * Change to use the `natural` package for `Natural` numbers.
src/Waargonaut/Decode.hs view
@@ -114,6 +114,7 @@ import           Data.Function                             (const, flip, ($),                                                             (&)) import           Data.Functor                              (fmap, (<$), (<$>))+import           Data.Functor.Alt                          ((<!>)) import           Data.Functor.Identity                     (Identity,                                                             runIdentity) import           Data.Monoid                               (mempty)@@ -123,7 +124,8 @@ import           Data.List.NonEmpty                        (NonEmpty ((:|))) import           Data.Maybe                                (Maybe (..),                                                             fromMaybe, maybe)-import           Natural                                   (Natural, replicate, zero', successor')+import           Natural                                   (Natural, replicate,+                                                            successor', zero')  import           Data.Text                                 (Text) @@ -466,8 +468,8 @@ -- -- myRecDecoder :: Decoder f MyRec -- myRecDecoder = MyRec---   <$> atKey "field_a" text---   <*> atKey "field_b" int+--   \<$> atKey "field_a" text+--   \<*> atKey "field_b" int -- @ -- atKey@@ -705,8 +707,9 @@   -> DecodeResult f (NonEmpty a) nonemptyAt elemD = down >=> \curs -> do   h <- focus elemD curs-  xs <- moveRight1 curs-  (h :|) <$> rightwardSnoc [] elemD xs+  DI.try (moveRight1 curs) >>= maybe+    (pure $ h :| [])+    (fmap (h :|) . rightwardSnoc [] elemD)  -- | Helper to create a 'NonEmpty a' 'Decoder'. nonempty :: Monad f => Decoder f a -> Decoder f (NonEmpty a)@@ -735,7 +738,7 @@   -> Decoder f (Maybe a)   -> Decoder f a withDefault def hasD =-  withCursor (fmap (fromMaybe def) . focus hasD)+  fromMaybe def <$> hasD  -- | Named to match it's 'Encoder' counterpart, this function will decode an -- optional value.@@ -744,7 +747,7 @@   => Decoder f a   -> Decoder f (Maybe a) maybeOrNull a =-  withCursor (DI.try . focus a)+  (Nothing <$ null) <!> (Just <$> a)  -- | Decode either an 'a' or a 'b', failing if neither 'Decoder' succeeds. The -- 'Right' decoder is attempted first.@@ -754,6 +757,4 @@   -> Decoder f b   -> Decoder f (Either a b) either leftD rightD =-  withCursor $ \c ->-    DI.try (focus (Right <$> rightD) c) >>=-    maybe (focus (Left <$> leftD) c) pure+  (Left <$> leftD) <!> (Right <$> rightD)
src/Waargonaut/Decode/Internal.hs view
@@ -329,10 +329,11 @@   go empty   where     go acc cur = do-      me <- fmap (scons acc) <$> try (runDecoder' elemD cur)-      maybe (pure acc)-        (\r -> try (mvCurs cur) >>= maybe (pure r) (go r))-        me+      acc' <- scons acc <$> runDecoder' elemD cur++      try (mvCurs cur) >>= maybe+        (pure acc')+        (go acc')  -- | -- Provide a generalised and low level way of turning a JSON object into a
src/Waargonaut/Encode.hs view
@@ -10,12 +10,16 @@ module Waargonaut.Encode   (     -- * Encoder type-    Encoder (Encoder)+    Encoder   , Encoder'+  , ObjEncoder+  , ObjEncoder'      -- * Creation   , encodeA   , encodePureA+  , jsonEncoder+  , objEncoder      -- * Runners   , runPureEncoder@@ -53,6 +57,9 @@   , keyValuesAsObj   , onObj   , keyValueTupleFoldable+  , extendObject+  , extendMapLikeObject+  , combineObjects      -- * Encoders specialised to Identity   , int'@@ -72,97 +79,75 @@   , mapToObj'   , keyValuesAsObj'   , json'-  , generaliseEncoder'-+  , generaliseEncoder   ) where  -import           Control.Monad.Morph        (MFunctor (..), generalize)+import           Control.Applicative                  (Applicative (..), (<$>))+import           Control.Category                     (id, (.))+import           Control.Lens                         (AReview, At, Index,+                                                       IxValue, Prism', at,+                                                       cons, review, ( # ),+                                                       (?~), _Empty, _Wrapped)+import qualified Control.Lens                         as L -import           Control.Applicative        (Applicative (..), (<$>))-import           Control.Category           (id, (.))-import           Control.Lens               (AReview, At, Index, IxValue,-                                             Prism', Rewrapped, Wrapped (..),-                                             at, cons, iso, review, ( # ), (?~),-                                             _Empty, _Wrapped)-import qualified Control.Lens               as L+import           Prelude                              (Bool, Int, Integral,+                                                       Monad, fromIntegral, fst) -import           Prelude                    (Bool, Int, Integral, Monad,-                                             fromIntegral)+import           Data.Foldable                        (Foldable, foldr, foldrM)+import           Data.Function                        (const, flip, ($), (&))+import           Data.Functor                         (Functor, fmap)+import           Data.Functor.Contravariant           ((>$<))+import           Data.Functor.Contravariant.Divisible (divide)+import           Data.Functor.Identity                (Identity (..))+import           Data.Traversable                     (Traversable, traverse) -import           Data.Foldable              (Foldable, foldr, foldrM)-import           Data.Function              (const, flip, ($), (&))-import           Data.Functor               (Functor, fmap)-import           Data.Functor.Contravariant (Contravariant (..), (>$<))-import           Data.Functor.Identity      (Identity (..))-import           Data.Traversable           (Traversable, traverse)+import           Data.Either                          (Either)+import qualified Data.Either                          as Either+import           Data.List.NonEmpty                   (NonEmpty)+import           Data.Maybe                           (Maybe)+import qualified Data.Maybe                           as Maybe+import           Data.Scientific                      (Scientific) -import           Data.Either                (Either)-import qualified Data.Either                as Either-import           Data.List.NonEmpty         (NonEmpty)-import           Data.Maybe                 (Maybe)-import qualified Data.Maybe                 as Maybe-import           Data.Scientific            (Scientific)+import           Data.Monoid                          (Monoid, mempty)+import           Data.Semigroup                       (Semigroup) -import           Data.Monoid                (Monoid, mempty)-import           Data.Semigroup             (Semigroup)+import qualified Data.ByteString.Builder              as BB+import           Data.ByteString.Lazy                 (ByteString) -import qualified Data.ByteString.Builder    as BB-import           Data.ByteString.Lazy       (ByteString)+import           Data.Map                             (Map)+import qualified Data.Map                             as Map -import           Data.Map                   (Map)-import qualified Data.Map                   as Map+import           Data.Text                            (Text) -import           Data.Text                  (Text)+import           Waargonaut.Encode.Types              (Encoder, Encoder',+                                                       ObjEncoder, ObjEncoder',+                                                       finaliseEncoding,+                                                       generaliseEncoder,+                                                       initialEncoding,+                                                       jsonEncoder, objEncoder,+                                                       runEncoder,+                                                       runPureEncoder) -import           Waargonaut.Types           (AsJType (..), JAssoc (..), JObject,-                                             Json, MapLikeObj (..), WS,-                                             textToJString, wsRemover,-                                             _JNumberInt, _JNumberScientific)-import           Waargonaut.Types.Json      (waargonautBuilder)+import           Waargonaut.Types                     (AsJType (..),+                                                       JAssoc (..), JObject,+                                                       Json, MapLikeObj (..),+                                                       WS, textToJString,+                                                       toMapLikeObj, wsRemover,+                                                       _JNumberInt,+                                                       _JNumberScientific)+import           Waargonaut.Types.Json                (waargonautBuilder) --- |--- Define an "encoder" as a function from some @a@ to some 'Json' with the--- allowance for some context @f@.----newtype Encoder f a = Encoder-  { runEncoder :: a -> f Json -- ^ Run this 'Encoder' to convert the 'a' to 'Json'-  } -instance (Encoder f a) ~ t => Rewrapped (Encoder f a) t--instance Wrapped (Encoder f a) where-  type Unwrapped (Encoder f a) = a -> f Json-  _Wrapped' = iso runEncoder Encoder--instance Contravariant (Encoder f) where-  contramap f (Encoder g) = Encoder (g . f)--instance MFunctor Encoder where-  hoist nat (Encoder eFn) = Encoder (nat . eFn)---- | Generalise an 'Encoder' a' to 'Encoder f a'-generaliseEncoder' :: Monad f => Encoder' a -> Encoder f a-generaliseEncoder' = Encoder . fmap generalize . runEncoder-{-# INLINE generaliseEncoder' #-}---- |--- As a convenience, this type is a pure Encoder over 'Identity' in place of the @f@.-type Encoder' = Encoder Identity- -- | Create an 'Encoder'' for 'a' by providing a function from 'a -> f Json'. encodeA :: (a -> f Json) -> Encoder f a-encodeA = Encoder+encodeA = jsonEncoder  -- | As 'encodeA' but specialised to 'Identity' when the additional flexibility -- isn't needed. encodePureA :: (a -> Json) -> Encoder' a encodePureA f = encodeA (Identity . f) --- | Run the given 'Encoder' to produce a lazy 'ByteString'.-runPureEncoder :: Encoder' a -> a -> Json-runPureEncoder enc = runIdentity . runEncoder enc- -- | Encode an @a@ directly to a 'ByteString' using the provided 'Encoder'. simpleEncodeNoSpaces   :: Applicative f@@ -174,7 +159,7 @@  -- | As per 'simpleEncodeNoSpaces' but specialised the 'f' to 'Data.Functor.Identity' and remove it. simplePureEncodeNoSpaces-  :: Encoder' a+  :: Encoder Identity a   -> a   -> ByteString simplePureEncodeNoSpaces enc =@@ -230,7 +215,8 @@ -- | Encode a 'Maybe' value, using the provided 'Encoder''s to handle the -- different choices. maybe-  :: Encoder f ()+  :: Functor f+  => Encoder f ()   -> Encoder f a   -> Encoder f (Maybe a) maybe encN = encodeA@@ -247,7 +233,8 @@  -- | Encode an 'Either' value using the given 'Encoder's either-  :: Encoder f a+  :: Functor f+  => Encoder f a   -> Encoder f b   -> Encoder f (Either a b) either eA = encodeA@@ -366,7 +353,7 @@   -> Encoder f a   -> Encoder f (t a) encodeWithInner f g =-  Encoder $ fmap f . traverse (runEncoder g)+  jsonEncoder $ fmap f . traverse (runEncoder g)  -- | As per 'traversable' but with the 'f' specialised to 'Data.Functor.Identity'. traversable'@@ -527,6 +514,67 @@   -> Encoder' i mapLikeObj' f = encodePureA $ \a ->   _JObj # (fromMapLikeObj $ f a (_Empty # ()), mempty)++-- |+-- This function allows you to extend the fields on a JSON object created by a+-- separate encoder.+--+extendObject+  :: Functor f+  => ObjEncoder f a+  -> a+  -> (JObject WS Json -> JObject WS Json)+  -> f Json+extendObject encA a f =+  finaliseEncoding encA . f <$> initialEncoding encA a++-- |+-- This function lets you extend the fields on a JSON object but enforces the+-- uniqueness of the keys by working through the 'MapLikeObj' structure.+--+-- This will keep the first occurence of each unique key in the map. So be sure+-- to check your output.+--+extendMapLikeObject+  :: Functor f+  => ObjEncoder f a+  -> a+  -> (MapLikeObj WS Json -> MapLikeObj WS Json)+  -> f Json+extendMapLikeObject encA a f =+  finaliseEncoding encA . floopObj <$> initialEncoding encA a+  where+    floopObj = fromMapLikeObj . f . fst . toMapLikeObj++-- |+-- Given encoders for things that are represented in JSON as 'objects', and a+-- way to get to the 'b' and 'c' from the 'a'. This function lets you create an+-- encoder for 'a'. The two objects are combined to make one single JSON object.+--+-- Given+--+-- @+-- encodeFoo :: ObjEncoder f Foo+-- encodeBar :: ObjEncoder f Bar+-- -- and some wrapping type:+-- data A = { _foo :: Foo, _bar :: Bar }+-- @+--+-- We can use this function to utilise our already defined 'ObjEncoder'+-- structures to give us an encoder for 'A':+--+-- @+-- combineObjects (\aRecord -> (_foo aRecord, _bar aRecord)) encodeFoo encodeBar :: ObjEncoder f Bar+-- @+--+combineObjects+  :: Applicative f+  => (a -> (b, c))+  -> ObjEncoder f b+  -> ObjEncoder f c+  -> ObjEncoder f a+combineObjects f eB eC =+  divide f eB eC  -- | When encoding a JSON object that may contain duplicate keys, this function -- works the same as the 'atKey' function for 'MapLikeObj'.
+ src/Waargonaut/Encode/Types.hs view
@@ -0,0 +1,129 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE NoImplicitPrelude #-}+-- |+-- Types and functions that make up the internal structure of the encoders.+--+module Waargonaut.Encode.Types+  ( -- * Types+    EncoderFns (..)++    -- * Useful aliases+  , Encoder+  , Encoder'+  , ObjEncoder+  , ObjEncoder'++    -- * Runners+  , runEncoder+  , runPureEncoder++    -- * Helpers+  , jsonEncoder+  , objEncoder+  , generaliseEncoder+  ) where++import           Control.Monad                        (Monad)+import           Control.Monad.Morph                  (MFunctor (..),+                                                       generalize)++import           Control.Applicative                  (Applicative, liftA2,+                                                       pure)+import           Control.Category                     (id, (.))+import           Control.Lens                         (( # ))++import           Data.Either                          (either)+import           Data.Function                        (const, ($))+import           Data.Functor                         (Functor)+import           Data.Functor.Contravariant           (Contravariant (..))++import           Data.Functor.Contravariant.Divisible (Decidable (..),+                                                       Divisible (..))+import           Data.Monoid                          (mempty)+import           Data.Semigroup                       ((<>))+import           Data.Void                            (absurd)++import           Data.Functor                         (fmap)+import           Data.Functor.Identity                (Identity (..))++import           Waargonaut.Types                     (JObject, Json, WS, _JObj)+++-- |+-- Define an "encoder" as a function from some @a@ to some 'Json' with the+-- allowance for some context @f@.+--+-- The helper functions 'jsonEncoder' and 'objEncoder' are probably what you+-- want to use.+--+data EncoderFns i f a = EncoderFns+  { finaliseEncoding :: i -> Json -- ^ The @i@ need not be the final 'Json' structure. This function will complete the output from 'initialEncoding' to the final 'Json' output.++  , initialEncoding  :: a -> f i -- ^ Run the initial encoding step of the given input. This lets you encode the @a@ to an intermediate structure before utilising the 'finaliseEncoding' function to complete the process.+  }++instance MFunctor (EncoderFns i) where+  hoist nat (EncoderFns f i) = EncoderFns f (nat . i)++-- | Generalise any 'Encoder' a' to 'Encoder f a'+generaliseEncoder :: Monad f => EncoderFns i Identity a -> EncoderFns i f a+generaliseEncoder (EncoderFns f i) = EncoderFns f (generalize . i)++instance Contravariant (EncoderFns o f) where+  contramap f e = EncoderFns (finaliseEncoding e) (initialEncoding e . f)+  {-# INLINE contramap #-}++instance Applicative f => Divisible (EncoderFns (JObject WS Json) f) where+  conquer = objEncoder (const (pure mempty))+  {-# INLINE conquer #-}++  divide atobc (EncoderFns _ oB) (EncoderFns _ oC) = objEncoder $ \a ->+    let+      (b,c) = atobc a+    in+      liftA2 (<>) (oB b) (oC c)+  {-# INLINE divide #-}++instance Applicative f => Decidable (EncoderFns (JObject WS Json) f) where+  lose f = objEncoder $ \a -> absurd (f a)+  {-# INLINE lose #-}++  choose split (EncoderFns _ oB) (EncoderFns _ oC) = objEncoder $ \a ->+    either oB oC (split a)+  {-# INLINE choose #-}++-- | As a convenience, this type defines the @i@ to be a specific 'Json' structure:+type Encoder f a = EncoderFns Json f a++-- | As a convenience, this type defines the @i@ to be a specific 'JObject WS Json' structure:+type ObjEncoder f a = EncoderFns (JObject WS Json) f a++-- | As a convenience, this type is a pure Encoder over 'Identity' in place of the @f@.+type Encoder' a = EncoderFns Json Identity a+-- | As a convenience, this type is a pure ObjEncoder over 'Identity' in place of the @f@.+type ObjEncoder' a = EncoderFns (JObject WS Json) Identity a++-- | Run any encoder to the 'Json' representation, allowing for some+-- 'Applicative' context @f@.+runEncoder :: Functor f => EncoderFns i f a -> a -> f Json+runEncoder e = fmap (finaliseEncoding e) . initialEncoding e+{-# INLINE runEncoder #-}++-- | Run any encoder to the 'Json' representation, with the context specialised+-- to 'Identity' for convenience.+runPureEncoder :: EncoderFns i Identity a -> a -> Json+runPureEncoder e = runIdentity . fmap (finaliseEncoding e) . initialEncoding e+{-# INLINE runPureEncoder #-}++-- | Helper function for creating an 'Encoder', provides the default+-- 'finaliseEncoding' function for 'Json' encoders.+jsonEncoder :: (a -> f Json) -> EncoderFns Json f a+jsonEncoder = EncoderFns id+{-# INLINE jsonEncoder #-}++-- | Helper function for creating a JSON 'object' 'Encoder'. Provides the+-- default 'finaliseEncoding' function for completing the 'JObject' to the+-- necessary 'Json' type.+objEncoder :: (a -> f (JObject WS Json)) -> EncoderFns (JObject WS Json) f a+objEncoder = EncoderFns (\o -> _JObj # (o, mempty))+{-# INLINE objEncoder #-}
src/Waargonaut/Types/CommaSep.hs view
@@ -39,8 +39,8 @@   , unconsCommaSep   ) where -import           Prelude                 (Eq, Int, Show (showsPrec), otherwise,-                                          showString, shows, (&&), (<=), (==))+import           Prelude                 (Eq, Int, Show (showsPrec),+                                          showString, shows, (&&), (==), (||))  import           Control.Applicative     (Applicative (..), liftA2, pure, (*>),                                           (<*), (<*>))@@ -318,12 +318,10 @@    ix _ _ c@(CommaSeparated _ Nothing) = pure c -  ix i f c@(CommaSeparated w (Just es))-    | i == 0 && es ^. elemsElems . to V.null =-      CommaSeparated w . Just <$> (es & elemsLast . traverse %%~ f)-    | i <= es ^. elemsElems . to length =-      CommaSeparated w . Just <$> (es & elemsElems . ix i . traverse %%~ f)-    | otherwise = pure c+  ix i f (CommaSeparated w (Just es)) = CommaSeparated w . Just <$>+    if i == 0 && es ^. elemsElems . to V.null || i == es ^. elemsElems . to length+    then es & elemsLast . traverse %%~ f+    else es & elemsElems . ix i . traverse %%~ f  -- | Convert a list of 'a' to a 'CommaSeparated' list, with no whitespace. fromList :: (Monoid ws, Semigroup ws) => [a] -> CommaSeparated ws a
src/Waargonaut/Types/JArray.hs view
@@ -17,11 +17,11 @@   , jArrayBuilder   ) where -import           Prelude                   (Eq, Show)+import           Prelude                   (Eq, Show, Int)  import           Control.Category          ((.)) import           Control.Error.Util        (note)-import           Control.Lens              (AsEmpty (..), Cons (..), Rewrapped,+import           Control.Lens              (AsEmpty (..), Cons (..), Rewrapped, Ixed (..), Index, IxValue,                                             Wrapped (..), cons, isn't, iso,                                             nearly, over, prism, to, ( # ),                                             (^.), (^?), _2, _Wrapped)@@ -83,6 +83,12 @@ instance (Semigroup ws, Monoid ws) => Monoid (JArray ws a) where   mempty = JArray mempty   mappend = (<>)++type instance IxValue (JArray ws a) = a+type instance Index (JArray ws a)   = Int++instance Ixed (JArray ws a) where+  ix i f (JArray cs) = JArray <$> ix i f cs  instance Bifunctor JArray where   bimap f g (JArray cs) = JArray (bimap f g cs)
src/Waargonaut/Types/JObject.hs view
@@ -23,22 +23,24 @@   , MapLikeObj   , toMapLikeObj   , fromMapLikeObj+  , _MapLikeObj      -- * Parser / Builder   , jObjectBuilder   , parseJObject   ) where -import           Prelude                   (Eq, Int, Show, elem, not, otherwise,-                                            (==))+import           Prelude                   (Eq, Int, Show, elem, fst, not,+                                            otherwise, (==))  import           Control.Applicative       ((<*), (<*>)) import           Control.Category          (id, (.))-import           Control.Lens              (AsEmpty (..), At (..), Index,-                                            IxValue, Ixed (..), Lens',-                                            Rewrapped, Wrapped (..), cons,-                                            isn't, iso, nearly, re, to, ( # ),-                                            (.~), (<&>), (^.), (^?), _Wrapped)+import           Control.Lens              (AsEmpty (..), At (..), Index, +                                            IxValue, Ixed (..), Lens', Prism',+                                            Rewrapped, Wrapped (..), cons, +                                            isn't, iso, nearly, prism', re, to,+                                            ( # ), (.~), (<&>), (^.), (^?),+                                            _Wrapped)  import           Control.Monad             (Monad) import           Data.Bifoldable           (Bifoldable (bifoldMap))@@ -201,6 +203,15 @@   { fromMapLikeObj :: JObject ws a -- ^ Access the underlying 'JObject'.   }   deriving (Eq, Show, Functor, Foldable, Traversable)++-- |+-- 'Prism' for working with a 'JObject' as a 'MapLikeObj'. This optic will keep+-- the first unique key on a given 'JObject' and this information is not+-- recoverable. If you want to create a 'MapLikeObj' from a 'JObject' and keep+-- what is removed, then use the 'toMapLikeObj' function.+--+_MapLikeObj :: (Semigroup ws, Monoid ws) => Prism' (JObject ws a) (MapLikeObj ws a)+_MapLikeObj = prism' fromMapLikeObj (Just . fst . toMapLikeObj)  instance MapLikeObj ws a ~ t => Rewrapped (MapLikeObj ws a) t 
src/Waargonaut/Types/Json.hs view
@@ -1,6 +1,5 @@ {-# LANGUAGE DeriveFoldable         #-} {-# LANGUAGE DeriveFunctor          #-}-{-# LANGUAGE DeriveGeneric          #-} {-# LANGUAGE DeriveTraversable      #-} {-# LANGUAGE FlexibleContexts       #-} {-# LANGUAGE FlexibleInstances      #-}@@ -30,16 +29,22 @@   , jsonWSTraversal   , jtypeTraversal   , jtypeWSTraversal++  -- * Optics+  , oat+  , oix+  , aix   ) where  -import           Prelude                     (Eq, Show)+import           Prelude                     (Eq, Int, Show)  import           Control.Applicative         (pure, (<$>), (<*>), (<|>)) import           Control.Category            (id, (.)) import           Control.Lens                (Prism', Rewrapped, Traversal,-                                              Traversal', Wrapped (..), iso,-                                              prism, traverseOf, _Wrapped)+                                              Traversal', Wrapped (..), at, iso,+                                              ix, prism, traverseOf, _1,+                                              _Wrapped)  import           Control.Monad               (Monad) @@ -53,12 +58,14 @@ import           Data.Function               (flip) import           Data.Functor                (Functor (..)) import           Data.Monoid                 (Monoid (..))-import           Data.Semigroup              ((<>))+import           Data.Semigroup              (Semigroup, (<>)) import           Data.Traversable            (Traversable (..)) import           Data.Tuple                  (uncurry)  import           Data.ByteString.Builder     (Builder) import qualified Data.ByteString.Builder     as BB+import           Data.Maybe                  (Maybe)+import           Data.Text                   (Text)  import           Text.Parser.Char            (CharParsing, text) @@ -67,7 +74,7 @@ import           Waargonaut.Types.JNumber    (JNumber, jNumberBuilder,                                               parseJNumber) import           Waargonaut.Types.JObject    (JObject (..), jObjectBuilder,-                                              parseJObject)+                                              parseJObject, _MapLikeObj) import           Waargonaut.Types.JString    (JString, jStringBuilder,                                               parseJString) import           Waargonaut.Types.Whitespace (WS (..), parseWhitespace)@@ -75,10 +82,17 @@ -- $setup -- >>> :set -XOverloadedStrings -- >>> import Utils+-- >>> import Control.Lens -- >>> import Control.Monad (return) -- >>> import Data.Either (Either (..), isLeft)+-- >>> import Data.Function (($)) -- >>> import Waargonaut.Decode.Error (DecodeError) -- >>> import Data.Digit (HeXDigit)+-- >>> import qualified Waargonaut.Encode as E+-- >>> let intList = E.runPureEncoder (E.list E.int) [1,2,3]+-- >>> data Foo = Foo { fooA :: Int, fooB :: Text } deriving Show+-- >>> let encodeFoo = E.mapLikeObj $ \(Foo i t) -> E.atKey' "a" E.int i . E.atKey' "b" E.text t+-- >>> let obj = E.runPureEncoder encodeFoo (Foo 33 "Fred") ----  -- | Individual JSON Types and their trailing whitespace.@@ -212,6 +226,38 @@ jTypesBuilder s (JStr js tws)   = jStringBuilder js                             <> s tws jTypesBuilder s (JArr js tws)   = jArrayBuilder s waargonautBuilder js          <> s tws jTypesBuilder s (JObj jobj tws) = jObjectBuilder s waargonautBuilder jobj       <> s tws++-- |+-- A 'Control.Lens.Traversal'' over the 'a' at the given 'Text' key on a JSON object.+--+-- >>> E.simplePureEncodeNoSpaces E.json (obj & oat "c" ?~ E.runPureEncoder E.int 33)+-- "{\"c\":33,\"a\":33,\"b\":\"Fred\"}"+-- >>> E.simplePureEncodeNoSpaces E.json (obj & oat "d" ?~ E.runPureEncoder E.text "sally")+-- "{\"d\":\"sally\",\"a\":33,\"b\":\"Fred\"}"+--+oat :: (AsJType r ws a, Semigroup ws, Monoid ws) => Text -> Traversal' r (Maybe a)+oat k = _JObj . _1 . _MapLikeObj . at k++-- |+-- A 'Control.Lens.Traversal'' over the 'a' at the given 'Int' position in a JSON object.+--+-- >>> E.simplePureEncodeNoSpaces E.json (obj & oix 0 .~ E.runPureEncoder E.int 1)+-- "{\"a\":1,\"b\":\"Fred\"}"+-- >>> E.simplePureEncodeNoSpaces E.json (obj & oix 1 .~ E.runPureEncoder E.text "sally")+-- "{\"a\":33,\"b\":\"sally\"}"+oix :: (Semigroup ws, Monoid ws, AsJType r ws a) => Int -> Traversal' r a+oix i = _JObj . _1 . ix i++-- |+-- A 'Control.Lens.Traversal'' over the 'a' at the given 'Int' position in a JSON array.+--+-- >>> E.simplePureEncodeNoSpaces E.json ((E.runPureEncoder (E.list E.int) [1,2,3]) & aix 0 .~ E.runPureEncoder E.int 99)+-- "[99,2,3]"+-- >>> E.simplePureEncodeNoSpaces E.json ((E.runPureEncoder (E.list E.int) [1,2,3]) & aix 2 .~ E.runPureEncoder E.int 44)+-- "[1,2,44]"+aix :: (AsJType r ws a, Semigroup ws, Monoid ws) => Int -> Traversal' r a+aix i = _JArr . _1 . ix i+  -- | Parse a 'null' value. --
test/Decoder.hs view
@@ -41,15 +41,47 @@  decoderTests :: TestTree decoderTests = testGroup "Decoding"-  [ testCase "Decode Image (test1.json)" decodeTest1Json-  , testCase "Decode [Int]" decodeTest2Json-  , testCase "Decode (Char,String,[Int])" decodeTest3Json-  , testCase "Decode Fail with Bad Key" decodeTestBadObjKey-  , testCase "Decode Fail with Missing Key" decodeTestMissingObjKey-  , testCase "Decode Enum and throwError" decodeTestEnumError-  , testCase "Decode Using Alt" decodeAlt-  , testCase "Decode Using Alt (Error) - Records BranchFail" decodeAltError+  [ testCase "Image (test1.json)" decodeTest1Json+  , testCase "[Int]" decodeTest2Json+  , testCase "(Char,String,[Int])" decodeTest3Json+  , testCase "Fail with Bad Key" decodeTestBadObjKey+  , testCase "Fail with Missing Key" decodeTestMissingObjKey+  , testCase "Enum and throwError" decodeTestEnumError+  , testCase "Using Alt" decodeAlt+  , testCase "Using Alt (Error) - Records BranchFail" decodeAltError+  , testCase "List Decoder" listDecoder+  , testCase "NonEmpty List Decoder" nonEmptyDecoder   ]++nonEmptyDecoder :: Assertion+nonEmptyDecoder = do+  let+    dec = D.runPureDecode (D.nonempty D.int) parseBS . D.mkCursor++    ok = "[1]"+    notOkay = "[]"++    badElem = "[1, \"fred\"]"++  assertBool "NonEmpty Decoder - fail! non-empty list decoder BROKEN. Start panicking" (Either.isRight (dec ok))+  assertBool "NonEmpty Decoder - empty list shouldn't succeed" (Either.isLeft (dec notOkay))+  assertBool "NonEmpty Decoder - invalid element decoder accepted" (Either.isLeft (dec badElem))++listDecoder :: Assertion+listDecoder = do+  let+    dec = D.runPureDecode (D.list D.int) parseBS . D.mkCursor++    ok = "[1,2,3]"+    okE = "[]"++    badShape = "{}"+    badElem = "[\"fred\", \"susan\"]"++  assertBool "List Decoder - fail! List Decoder BROKEN. Start panicking." (Either.isRight (dec ok))+  assertBool "List Decoder - empty list fail" (Either.isRight (dec okE))+  assertBool "List Decoder - move down should return empty list" (Either.isRight (dec badShape))+  assertBool "List Decoder - invalid element decoder accepted" (Either.isLeft (dec badElem))  decodeTestMissingObjKey :: Assertion decodeTestMissingObjKey = do
test/Decoder/Laws.hs view
@@ -4,7 +4,7 @@ {-# LANGUAGE ScopedTypeVariables        #-} module Decoder.Laws (decoderLaws) where -import           Control.Applicative     (Applicative, liftA3, pure)+import           Control.Applicative     (Applicative, pure) import           Control.Monad.Except    (throwError)  import           Data.Functor.Alt        (Alt ((<!>)))@@ -14,8 +14,6 @@ import           Test.Tasty.Hedgehog     (testProperty)  import           Hedgehog-import           Hedgehog.Function       (Arg, Vary)-import qualified Hedgehog.Function       as Fn import qualified Hedgehog.Gen            as Gen  import qualified Waargonaut.Decode       as D@@ -24,15 +22,12 @@  import           Types.Common            (parseBS) +import qualified Laws+ runD :: Decoder Identity a -> Either (DecodeError, D.CursorHistory) a runD d = D.runPureDecode d parseBS (D.mkCursor "true") -runSD :: ShowDecoder a -> Either (DecodeError, D.CursorHistory) a-runSD = runD . unShowDecoder--newtype ShowDecoder a = SD-  { unShowDecoder :: Decoder Identity a-  }+newtype ShowDecoder a = SD (Decoder Identity a)   deriving (Functor, Monad, Applicative)  instance Alt ShowDecoder where@@ -47,181 +42,33 @@ genShowDecoder :: Gen a -> Gen (ShowDecoder a) genShowDecoder genA = Gen.choice   [ SD . pure <$> genA-  , SD <$> Gen.constant (throwError $ ConversionFailure "Intentional DecodeError (TEST)")+  , SD        <$> Gen.constant (throwError $ ConversionFailure "Intentional DecodeError (TEST)")   ] --- |--- Alt Associative--- <!> is associative:             (a <!> b) <!> c = a <!> (b <!> c)----alt_associativity :: Property-alt_associativity = property $ do-  (a,b,c) <- forAll $ liftA3 (,,)-    (genShowDecoder Gen.bool)-    (genShowDecoder Gen.bool)-    (genShowDecoder Gen.bool)--  runSD ((a <!> b) <!> c) === runSD (a <!> (b <!> c))---- |--- Alt left distributes--- <$> left-distributes over <!>:  f <$> (a <!> b) = (f <$> a) <!> (f <$> b)-alt_left_distributes-  :: forall a b.-     ( Show a, Arg a, Vary a, Eq a-     , Show b, Arg b, Vary b, Eq b-     )-  => Gen a-  -> Gen b-  -> Property-alt_left_distributes genA genB = property $ do-  f <- Fn.forAllFn $ Fn.fn genA--  a <- forAll (genShowDecoder genB)-  b <- forAll (genShowDecoder genB)--  runSD ( f <$> (a <!> b) ) === runSD ( (f <$> a) <!> (f <$> b) )---- |--- identity------     pure id <*> v = v-applicative_id :: Property-applicative_id = property $ do-  a <- forAll (genShowDecoder Gen.bool)-  runSD (pure id <*> a) === runSD a---- |--- composition------     pure (.) <*> u <*> v <*> w = u <*> (v <*> w)-applicative_composition-  :: forall a b c.-     ( Show a, Arg a, Vary a, Eq a-     , Show b, Arg b, Vary b, Eq b-     , Show c, Arg c, Vary c-     )-  => Gen a-  -> Gen b-  -> Gen c-  -> Property-applicative_composition genA genB genC = property $ do-  u <- Fn.forAllFn $ Fn.fn genB-  v <- Fn.forAllFn $ Fn.fn genC--  w <- forAll (genShowDecoder genA)--  let-    dU = pure u-    dV = pure v--  runSD ( pure (.) <*> dU <*> dV <*> w ) === runSD ( dU <*> ( dV <*> w ) )---- |--- homomorphism------     pure f <*> pure x = pure (f x)-applicative_homomorphism-  :: forall a b.-     ( Show a, Arg a, Vary a, Eq a-     , Show b, Arg b, Vary b-     )-  => Gen a-  -> Gen b-  -> Property-applicative_homomorphism genA genB = property $ do-  f <- Fn.forAllFn $ Fn.fn genA-  x <- forAll genB--  runD (pure f <*> pure x) === runD (pure (f x))---- |--- interchange------     u <*> pure y = pure ($ y) <*> u-applicative_interchange-  :: forall u y.-     ( Show u, Arg u, Vary u, Eq u-     , Show y, Arg y, Vary y-     )-  => Gen u-  -> Gen y-  -> Property-applicative_interchange genU genY = property $ do-  u <- Fn.forAllFn $ Fn.fn genU-  y <- forAll genY--  let-    dU = pure u--  runD (dU <*> pure y) === runD (pure ($ y) <*> dU)---- |--- monad------    return a >>= k = k a-monad_return_bind-  :: forall a k.-     ( Show a, Arg a, Vary a, Eq a-     , Show k, Arg k, Vary k, Eq k-     )-  => Gen a-  -> Gen k-  -> Property-monad_return_bind genA genK = property $ do-  k' <- Fn.forAllFn $ Fn.fn genK-  a <- forAll genA--  let-    k = SD . pure . k'--  runSD (return a >>= k) === runSD (k a)---- |--- monad------     m >>= return  =  m-monad_bind_return :: Property-monad_bind_return = property $ do-  m <- forAll (genShowDecoder Gen.bool)--  runSD (m >>= return) === runSD m---- |--- monad------     m >>= (\x -> k x >>= h)  =  (m >>= k) >>= h-monad_associativity-  :: forall m k h.-     ( Show m, Arg m, Vary m, Eq m-     , Show k, Arg k, Vary k, Eq k-     , Show h, Arg h, Vary h, Eq h-     )-  => Gen m-  -> Gen k-  -> Gen h-  -> Property-monad_associativity genM genK genH = property $ do-  m <- forAll (genShowDecoder genM)+decoderLaws :: TestTree+decoderLaws = testGroup "Decoder Laws"+  [ testGroup "Applicative" -  k' <- Fn.forAllFn $ Fn.fn genK-  h' <- Fn.forAllFn $ Fn.fn genH+    [ testProperty "identity"     $ Laws.applicative_id genShowDecoder Gen.bool+    , testProperty "composition"  $ Laws.applicative_composition genShowDecoder Gen.bool Gen.bool Gen.bool+    , testProperty "homomorphism" $ Laws.applicative_homomorphism sdPure Gen.bool Gen.bool+    , testProperty "interchange"  $ Laws.applicative_interchange sdPure Gen.bool Gen.bool+    ] -  let-    k = SD . pure . k'-    h = SD . pure . h'+  , testGroup "Alt"+    [ testProperty "associativity"    $ Laws.alt_associativity genShowDecoder Gen.bool+    , testProperty "left distributes" $ Laws.alt_left_distributes genShowDecoder Gen.bool Gen.bool+    ] -  runSD (m >>= (\x -> k x >>= h)) === runSD ( (m >>= k) >>= h )+  , testGroup "Monad"+    [ testProperty "return a >>= k = k a" $ Laws.monad_return_bind genShowDecoder Gen.bool Gen.bool+    , testProperty "m >>= return = m"     $ Laws.monad_bind_return_id genShowDecoder Gen.bool+    , testProperty "associativity"        $ Laws.monad_associativity genShowDecoder Gen.bool Gen.bool Gen.bool+    ] -decoderLaws :: TestTree-decoderLaws = testGroup "Decoder Laws"-  [ testProperty "Applicative 'identity'" applicative_id-  , testProperty "Applicative 'composition'" $ applicative_composition Gen.bool Gen.bool Gen.bool-  , testProperty "Applicative 'homomorphism'" $ applicative_homomorphism Gen.bool Gen.bool-  , testProperty "Applicative 'interchange'" $ applicative_interchange Gen.bool Gen.bool-  , testProperty "Alt 'associativity'" alt_associativity-  , testProperty "Alt 'left distributes'" $ alt_left_distributes Gen.bool Gen.bool-  , testProperty "Monad 'return a >>= k = k a'" $ monad_return_bind Gen.bool Gen.bool-  , testProperty "Monad 'm >>= return = m'" monad_bind_return-  , testProperty "Monad 'associativity'" $ monad_associativity Gen.bool Gen.bool Gen.bool+  , testGroup "Functor"+    [ testProperty "'fmap compose'" $ Laws.fmap_compose genShowDecoder Gen.bool Gen.bool Gen.bool+    ]   ]+  where+    sdPure = (pure :: a -> ShowDecoder a)
test/Encoder.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE DeriveGeneric             #-} {-# LANGUAGE FlexibleContexts          #-} {-# LANGUAGE NoMonomorphismRestriction #-} {-# LANGUAGE OverloadedStrings         #-}@@ -8,20 +7,24 @@   , testImageDataType   ) where -import           Test.Tasty           (TestName, TestTree, testGroup)-import           Test.Tasty.HUnit     (assertEqual, testCase)+import           Control.Lens          ((<&>), (?~)) -import           Data.Proxy           (Proxy (..))+import           Test.Tasty            (TestName, TestTree, testGroup)+import           Test.Tasty.HUnit      (testCase, (@?=)) -import           Waargonaut.Encode    (Encoder, Encoder')-import qualified Waargonaut.Encode    as E+import           Data.Proxy            (Proxy (..)) -import           Data.ByteString.Lazy (ByteString)+import           Waargonaut.Encode     (Encoder, Encoder')+import qualified Waargonaut.Encode     as E -import           Types.Common         (Image (..), testFudge, testImageDataType)+import           Data.ByteString.Lazy  (ByteString) -import           Waargonaut.Generic   (GWaarg, mkEncoder, proxy)+import           Types.Common          (Image (..), Overlayed (..), testFudge,+                                        testImageDataType) +import           Waargonaut.Generic    (GWaarg, mkEncoder, proxy)+import           Waargonaut.Types.Json (oat)+ testImageEncodedNoSpaces :: ByteString testImageEncodedNoSpaces = "{\"Width\":800,\"Height\":600,\"Title\":\"View from 15th Floor\",\"Animated\":false,\"IDs\":[116,943,234,38793]}" @@ -37,20 +40,33 @@ testFudgeEncodedWithConsName :: ByteString testFudgeEncodedWithConsName = "{\"fudgey\":\"Chocolate\"}" +testOverlayed :: Overlayed+testOverlayed = Overlayed "fred" testFudge++testOverlayedOut :: ByteString+testOverlayedOut = "{\"id\":\"fred\",\"fudgey\":\"Chocolate\"}"++encodeOverlay :: Applicative f => Encoder f Overlayed+encodeOverlay = E.encodeA $ \(Overlayed i f) -> E.runEncoder fudgeEnc f+  <&> oat "id" ?~ E.runPureEncoder E.text i+  where+    fudgeEnc = proxy mkEncoder (Proxy :: Proxy GWaarg)+ tCase   :: TestName   -> Encoder' a   -> a   -> ByteString   -> TestTree-tCase nm enc a =-  testCase nm . assertEqual nm (E.simplePureEncodeNoSpaces enc a)+tCase nm enc a expected = testCase nm $+  E.simplePureEncodeNoSpaces enc a @?= expected  encoderTests :: TestTree encoderTests = testGroup "Encoder"-  [ tCase "Encode Image" encodeImage testImageDataType testImageEncodedNoSpaces-  , tCase "Encode Image (Generic)" enc testImageDataType testImageEncodedNoSpaces-  , tCase "Encode newtype - with constructor name" enc testFudge testFudgeEncodedWithConsName+  [ tCase "Image" encodeImage testImageDataType testImageEncodedNoSpaces+  , tCase "Image (Generic)" enc testImageDataType testImageEncodedNoSpaces+  , tCase "newtype - with constructor name" enc testFudge testFudgeEncodedWithConsName+  , tCase "Overlayed" encodeOverlay testOverlayed testOverlayedOut   ]   where     enc = proxy mkEncoder (Proxy :: Proxy GWaarg)
test/Encoder/Laws.hs view
@@ -2,20 +2,22 @@ {-# LANGUAGE RankNTypes       #-} module Encoder.Laws (encoderLaws) where -import           Test.Tasty            (TestTree, testGroup)-import           Test.Tasty.Hedgehog   (testProperty)+import           Test.Tasty                 (TestTree, testGroup)+import           Test.Tasty.Hedgehog        (testProperty) -import           Data.ByteString.Lazy  (ByteString)-import           Data.Functor.Identity (Identity)+import           Data.ByteString.Lazy       (ByteString)+import           Data.Functor.Contravariant (contramap)+import           Data.Functor.Identity      (Identity)  import           Hedgehog-import           Hedgehog.Function     (Arg, Vary)-import qualified Hedgehog.Function     as Fn-import qualified Hedgehog.Gen          as Gen+import qualified Hedgehog.Function          as Fn+import qualified Hedgehog.Gen               as Gen -import           Waargonaut.Encode     (Encoder)-import qualified Waargonaut.Encode     as E+import           Waargonaut.Encode          (Encoder)+import qualified Waargonaut.Encode          as E +import qualified Laws+ runSE :: ShowEncoder a -> a -> ByteString runSE (SE e) = E.simplePureEncodeNoSpaces e @@ -27,39 +29,15 @@ instance Fn.Contravariant ShowEncoder where   contramap f (SE a) = SE (Fn.contramap f a) --- |--- contravariant------     contramap f . contramap g = contramap (g . f)-contravariant_composition-  :: forall f a.-     ( Show f, Arg f, Vary f, Eq f-     , Show a, Arg a, Vary a-     )-  => Gen f-  -> Gen Bool-  -> Gen a-  -> Property-contravariant_composition genF genG genA = property $ do-  f <- Fn.forAllFn $ Fn.fn genF-  g <- Fn.forAllFn $ Fn.fn genG--  let ea = SE E.bool--  a <- forAll genA--  runSE (Fn.contramap f $ Fn.contramap g ea) a === runSE (Fn.contramap (g . f) ea) a--contravariant_identity :: Property-contravariant_identity = property $ do-  a <- forAll Gen.bool--  let ea = SE E.bool--  runSE (Fn.contramap id ea) a === runSE ea a+genShowEncoder :: Encoder Identity a -> Gen a -> Gen (ShowEncoder a)+genShowEncoder enc _ = Gen.constant (SE enc)  encoderLaws :: TestTree encoderLaws = testGroup "Encoder Laws"-  [ testProperty "Contravariant 'composition'" $ contravariant_composition Gen.bool Gen.bool Gen.bool-  , testProperty "Contravariant 'identity'" contravariant_identity+  [ testGroup "Contravariant"+    [ testProperty "composition"+      $ Laws.contravariant_composition_with_run (genShowEncoder E.bool) runSE Gen.bool (Gen.maybe Gen.bool) Gen.bool+    , testProperty "identity"+      $ Laws.contravariant_identity_with_run (genShowEncoder E.bool) runSE Gen.bool+    ]   ]
+ test/Laws.hs view
@@ -0,0 +1,351 @@+{-# LANGUAGE RankNTypes          #-}+{-# LANGUAGE ScopedTypeVariables #-}+module Laws+  ( fmap_compose++  , alt_left_distributes+  , alt_associativity++  , applicative_id+  , applicative_composition+  , applicative_homomorphism+  , applicative_interchange++  , monad_return_bind+  , monad_bind_return_id+  , monad_associativity++  , contravariant_identity+  , contravariant_composition+  , contravariant_identity_with_run+  , contravariant_composition_with_run+  ) where++import           Control.Applicative        (liftA3)++import           Data.Functor.Alt           (Alt (..))+import           Data.Functor.Contravariant (Contravariant, contramap)++import           Hedgehog+import           Hedgehog.Function          (Arg, Vary)+import qualified Hedgehog.Function          as Fn++fmap_compose+  :: forall f a b c+   . ( Functor f+     , Show (f a)+     , Show a, Arg a, Vary a+     , Show b, Arg b, Vary b+     , Show c+     , Eq (f c)+     , Show (f c)+     )+  => (forall x. Gen x -> Gen (f x))+  -> Gen a+  -> Gen b+  -> Gen c+  -> Property+fmap_compose genF genA genB genC = property $ do+  g <- Fn.forAllFn $ Fn.fn genB+  f <- Fn.forAllFn $ Fn.fn genC+  xs <- forAll $ genF genA+  fmap (f . g) xs === fmap f (fmap g xs)++-- |+-- Alt left distributes+-- <$> left-distributes over <!>:  f <$> (a <!> b) = (f <$> a) <!> (f <$> b)+alt_left_distributes+  :: forall a b f.+     ( Alt f+     , Show a, Arg a, Vary a, Eq a+     , Show b, Arg b, Vary b, Eq b+     , Show (f a), Eq (f a)+     , Show (f b)+     )+  => (forall x. Gen x -> Gen (f x))+  -> Gen a+  -> Gen b+  -> Property+alt_left_distributes genF genA genB = property $ do+  f <- Fn.forAllFn $ Fn.fn genA++  a <- forAll (genF genB)+  b <- forAll (genF genB)++  (f <$> (a <!> b)) === ((f <$> a) <!> (f <$> b))++-- |+-- Alt Associative+-- <!> is associative:             (a <!> b) <!> c = a <!> (b <!> c)+--+alt_associativity+  :: forall f a.+     ( Alt f+     , Show (f a), Eq (f a)+     )+  => (forall x. Gen x -> Gen (f x))+  -> Gen a+  -> Property+alt_associativity genF genA = property $ do+  (a,b,c) <- forAll $ liftA3 (,,)+    (genF genA)+    (genF genA)+    (genF genA)++  ((a <!> b) <!> c) === (a <!> (b <!> c))++-- |+-- identity+--+--     pure id <*> v = v+applicative_id+  :: forall f a.+     ( Applicative f+     , Show (f a)+     , Eq (f a)+     )+  => (forall x. Gen x -> Gen (f x))+  -> Gen a+  -> Property+applicative_id genF genA = property $ do+  a <- forAll (genF genA)+  (pure id <*> a) === a++-- |+-- composition+--+--     pure (.) <*> u <*> v <*> w = u <*> (v <*> w)+applicative_composition+  :: forall f a b c.+     ( Show a, Arg a, Vary a, Eq a+     , Show b, Arg b, Vary b, Eq b+     , Show c, Arg c, Vary c+     , Show (f a)+     , Show (f b)+     , Show (f c)+     , Eq (f a)+     , Eq (f b)+     , Eq (f c)+     , Applicative f+     )+  => (forall x. Gen x -> Gen (f x))+  -> Gen a+  -> Gen b+  -> Gen c+  -> Property+applicative_composition genF genA genB genC = property $ do+  u <- Fn.forAllFn $ Fn.fn genB+  v <- Fn.forAllFn $ Fn.fn genC++  w <- forAll (genF genA)++  let+    dU = pure u+    dV = pure v++  ( pure (.) <*> dU <*> dV <*> w ) === ( dU <*> ( dV <*> w ) )++-- |+-- homomorphism+--+--     pure f <*> pure x = pure (f x)+applicative_homomorphism+  :: forall f a b.+     ( Show a, Arg a, Vary a, Eq a+     , Show b, Arg b, Vary b+     , Show (f a), Eq (f a)+     , Show (f a), Eq (f b)+     , Applicative f+     )+  => (forall x. x -> f x)+  -> Gen a+  -> Gen b+  -> Property+applicative_homomorphism pureF genA genB = property $ do+  f <- Fn.forAllFn $ Fn.fn genA+  x <- forAll genB++  (pureF f <*> pureF x) === (pureF (f x))++-- |+-- interchange+--+--     u <*> pure y = pure ($ y) <*> u+applicative_interchange+  :: forall f u y.+     ( Applicative f+     , Show u, Arg u, Vary u, Eq u+     , Show y, Arg y, Vary y+     , Show (f u), Eq (f u)+     , Show (f y), Eq (f y)+     )+  => (forall x. x -> f x)+  -> Gen u+  -> Gen y+  -> Property+applicative_interchange pureF genU genY = property $ do+  u <- Fn.forAllFn $ Fn.fn genU+  y <- forAll genY++  let+    dU = pureF u++  (dU <*> pure y) === (pure ($ y) <*> dU)++-- |+-- monad+--+--    return a >>= k = k a+monad_return_bind+  :: forall f a k.+     ( Monad f+     , Show a, Arg a, Vary a, Eq a+     , Show k, Arg k, Vary k, Eq k+     , Show (f a), Eq (f a)+     , Show (f k), Eq (f k)+     )+  => (forall x. Gen x -> Gen (f x))+  -> Gen a+  -> Gen k+  -> Property+monad_return_bind genF genA genK = property $ do+  k <- Fn.forAllFn $ Fn.fn (genF genK)+  a <- forAll genA++  (return a >>= k) === (k a)++-- |+-- monad+--+--     m >>= return  =  m+monad_bind_return_id+  :: forall f a.+     ( Monad f+     , Show a, Eq a+     , Show (f a), Eq (f a)+     )+  => (forall x. Gen x -> Gen (f x))+  -> Gen a+  -> Property+monad_bind_return_id genF genA = property $ do+  m <- forAll (genF genA)++  (m >>= return) === m++-- |+-- monad+--+--     m >>= (\x -> k x >>= h)  =  (m >>= k) >>= h+monad_associativity+  :: forall f m k h.+     ( Monad f+     , Show m, Arg m, Vary m, Eq m+     , Show k, Arg k, Vary k, Eq k+     , Show h, Arg h, Vary h, Eq h+     , Show (f m), Eq (f m)+     , Show (f k), Eq (f k)+     , Show (f h), Eq (f h)+     )+  => (forall x. Gen x -> Gen (f x))+  -> Gen m+  -> Gen k+  -> Gen h+  -> Property+monad_associativity genF genM genK genH = property $ do+  m <- forAll (genF genM)++  k <- Fn.forAllFn $ Fn.fn (genF genK)+  h <- Fn.forAllFn $ Fn.fn (genF genH)++  (m >>= (\x -> k x >>= h)) === ( (m >>= k) >>= h )++-- |+-- contravariant+--+--     contramap f . contramap g = contramap (g . f)+contravariant_composition+  :: forall f a b c.+     ( Contravariant f+     , Show a, Arg a, Vary a, Eq (f a), Show (f a)+     , Show b, Arg b, Vary b, Eq b+     , Show c, Show (f c)+     )+  => (forall x. Gen x -> Gen (f x))+  -> Gen a+  -> Gen b+  -> Gen c+  -> Property+contravariant_composition genF _genA genB genC = property $ do+  f <- Fn.forAllFn $ (Fn.fn genB :: Gen (Fn.Fn a b))+  g <- Fn.forAllFn $ (Fn.fn genC :: Gen (Fn.Fn b c))++  fc <- forAll (genF genC)++  (contramap f . contramap g) fc === contramap (g . f) fc++-- |+-- contravariant+--+--     contramap id a = a+contravariant_identity+  :: forall f a.+     ( Contravariant f+     , Show a, Arg a, Vary a+     , Show (f a)+     , Eq (f a)+     )+  => (forall x. Gen x -> Gen (f x))+  -> Gen a+  -> Property+contravariant_identity genF genA = property $ do+  a <- forAll (genF genA)++  contramap id a === a++-- |+-- contravariant+--+--     contramap f . contramap g = contramap (g . f)+contravariant_composition_with_run+  :: forall f a b c x.+     ( Contravariant f+     , Show a, Arg a, Vary a+     , Show b, Arg b, Vary b, Eq b+     , Show c, Show (f c)+     , Eq x, Show x+     )+  => (Gen c -> Gen (f c))+  -> (f a -> a -> x)+  -> Gen a+  -> Gen b+  -> Gen c+  -> Property+contravariant_composition_with_run genF runF genA genB genC = property $ do+  f <- Fn.forAllFn $ (Fn.fn genB :: Gen (Fn.Fn a b))+  g <- Fn.forAllFn $ (Fn.fn genC :: Gen (Fn.Fn b c))++  a <- forAll genA+  fc <- forAll (genF genC)++  runF ((contramap f . contramap g) fc) a === runF (contramap (g . f) fc) a++-- |+-- contravariant+--+--     contramap id a = a+contravariant_identity_with_run+  :: forall f a b.+     ( Contravariant f+     , Show a, Arg a, Vary a, Eq a+     , Show b, Eq b+     , Show (f a)+     )+  => (Gen a -> Gen (f a))+  -> (f a -> a -> b)+  -> Gen a+  -> Property+contravariant_identity_with_run genF runF genA = property $ do+  fa <- forAll (genF genA)+  a <- forAll genA++  runF (contramap id fa) a === runF fa a
test/Types/Common.hs view
@@ -31,6 +31,7 @@   , Image (..)   , Fudge (..)   , HasImage (..)+  , Overlayed (..)   ) where  import           Generics.SOP                (Generic, HasDatatypeInfo)@@ -161,6 +162,13 @@  testFudge :: Fudge testFudge = Fudge "Chocolate"++data Overlayed = Overlayed+  { _overId :: Text+  , _overFu :: Fudge+  }+  deriving (Show, GHC.Generic)+ genDecimalDigit :: Gen DecDigit genDecimalDigit = Gen.element decimalDigit 
waargonaut.cabal view
@@ -10,7 +10,7 @@ -- PVP summary:      +-+------- breaking API changes --                   | | +----- non-breaking API additions --                   | | | +--- code changes with no API change-version:             0.3.0.0+version:             0.4.0.0  -- A short (one-line) description of the package. synopsis:            JSON wrangling@@ -88,6 +88,7 @@                      , Waargonaut.Decode.ZipperMove                      , Waargonaut.Decode.Error                      , Waargonaut.Decode.Types+                     , Waargonaut.Encode.Types                       , Waargonaut.Decode                      , Waargonaut.Decode.Traversal@@ -167,6 +168,7 @@                      , Types.Json                      , Types.Whitespace +                     , Laws                      , Utils                      , Encoder                      , Encoder.Laws@@ -200,6 +202,7 @@                      , semigroupoids          >= 5.2.2  && < 6                      , containers             >= 0.5.6  && < 0.7                      , natural                >= 0.3    && < 4+                     , contravariant       >= 1.4     && < 2                       , waargonaut