packages feed

quantification-aeson (empty) → 0.8

raw patch · 4 files changed

+313/−0 lines, 4 filesdep +aesondep +basedep +containerssetup-changed

Dependencies added: aeson, base, containers, quantification, unordered-containers, vector

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Andrew Martin (c) 2016++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Andrew Martin nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ quantification-aeson.cabal view
@@ -0,0 +1,31 @@+cabal-version: 2.4+name: quantification-aeson+version: 0.8+synopsis: Rage against the quantification - integration with Aeson+description: Data types and typeclasses to deal with universally and existentially quantified types+homepage: https://github.com/andrewthad/quantification#readme+license: BSD-3-Clause+license-file: LICENSE+author: Andrew Martin, Marseille Bouchard+maintainer: andrew.thaddeus@gmail.com+copyright: 2018 Andrew Martin+category: Web+build-type: Simple++library+  hs-source-dirs: src+  exposed-modules:+    Data.Exists.Aeson+  build-depends:+    , aeson >= 2.2 && < 2.3+    , base >= 4.11.1 && < 5+    , containers >= 0.5 && < 0.7+    , quantification >=0.8 && <0.9+    , unordered-containers >= 0.2 && < 0.3+    , vector >= 0.11 && < 0.14+  default-language: Haskell2010+  ghc-options: -O2 -Wall++source-repository head+  type:     git+  location: https://github.com/andrewthad/quantification
+ src/Data/Exists/Aeson.hs view
@@ -0,0 +1,250 @@+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}++{-# OPTIONS_GHC -fno-warn-orphans #-}++module Data.Exists.Aeson+  ( FromJSONForall(..)+  , FromJSONForeach(..)+  , FromJSONExists(..)+  , ToJSONForall(..)+  , ToJSONForeach(..)+  , ToJSONKeyFunctionForall(..)+  , FromJSONKeyFunctionForeach(..)+  , ToJSONKeyForall(..)+  , ToJSONKeyForeach(..)+  , FromJSONKeyExists(..)+  , FromJSONKeyForeach(..)+  , ToJSONSing(..)+  , FromJSONSing(..)+  , parseJSONMapForeachKey+  , toJSONMapForeachKey+  ) where++import Control.Applicative (Const(..))+import Data.Aeson ((<?>))+import Data.Aeson (ToJSON(..),FromJSON(..))+import Data.Aeson (ToJSONKey(..),FromJSONKey(..))+import Data.Aeson (ToJSONKeyFunction(..),FromJSONKeyFunction(..))+import Data.Aeson.Types (JSONPathElement(Key,Index))+import Data.Coerce (coerce)+import Data.Exists (Exists(..),Some(..),Sing,ApplyForeach(..),OrdForeach)+import Data.Exists (Reify(..),Unreify(..))+import Data.Functor.Compose (Compose(..))+import Data.Functor.Product (Product(..))+import Data.Kind (Type)+import Data.Map.Strict (Map)++import qualified Data.Aeson.Encoding as Aeson+import qualified Data.Aeson.Encoding.Internal as AEI+import qualified Data.Aeson.Key as Key+import qualified Data.Aeson.KeyMap as KM+import qualified Data.Aeson.Types as Aeson+import qualified Data.HashMap.Strict as HM+import qualified Data.Map.Strict as M+import qualified Data.Traversable as TRV+import qualified Data.Vector as V++data ToJSONKeyFunctionForall f+  = ToJSONKeyTextForall !(forall a. f a -> Aeson.Key) !(forall a. f a -> Aeson.Encoding' Aeson.Key)+  | ToJSONKeyValueForall !(forall a. f a -> Aeson.Value) !(forall a. f a -> Aeson.Encoding)+data FromJSONKeyFunctionForeach f+  = FromJSONKeyTextParserForeach !(forall a. Sing a -> Aeson.Key -> Aeson.Parser (f a))+  | FromJSONKeyValueForeach !(forall a. Sing a -> Aeson.Value -> Aeson.Parser (f a))++instance (ToJSONForeach f, Reify a) => ToJSON (ApplyForeach f a) where+  toJSON = toJSONForeach reify++instance (FromJSONForeach f, Reify a) => FromJSON (ApplyForeach f a) where+  parseJSON = parseJSONForeach reify++instance ToJSONForeach f => ToJSONForeach (ApplyForeach f) where+  toJSONForeach s (ApplyForeach x) = toJSONForeach s x++instance FromJSONForeach f => FromJSONForeach (ApplyForeach f) where+  parseJSONForeach s = fmap ApplyForeach . parseJSONForeach s++instance ToJSONKeyForeach f => ToJSONKeyForeach (ApplyForeach f) where+  toJSONKeyForeach = case toJSONKeyForeach of+    ToJSONKeyTextForall f g -> ToJSONKeyTextForall+      (\(Pair s (ApplyForeach x)) -> f (Pair s x))+      (\(Pair s (ApplyForeach x)) -> g (Pair s x))+    ToJSONKeyValueForall f g -> ToJSONKeyValueForall+      (\(Pair s (ApplyForeach x)) -> f (Pair s x))+      (\(Pair s (ApplyForeach x)) -> g (Pair s x))++instance FromJSONKeyForeach f => FromJSONKeyForeach (ApplyForeach f) where+  fromJSONKeyForeach = case fromJSONKeyForeach of+    FromJSONKeyTextParserForeach f -> FromJSONKeyTextParserForeach (\s t -> fmap ApplyForeach (f s t))+    FromJSONKeyValueForeach f -> FromJSONKeyValueForeach (\s t -> fmap ApplyForeach (f s t))++instance (ToJSONKeyForeach f, Reify a) => ToJSONKey (ApplyForeach f a) where+  toJSONKey = case toJSONKeyForeach of+    ToJSONKeyTextForall toText toEnc -> ToJSONKeyText+      (\(ApplyForeach x) -> toText (Pair reify x))+      (\(ApplyForeach x) -> toEnc (Pair reify x))+    ToJSONKeyValueForall toValue toEnc -> ToJSONKeyValue+      (\(ApplyForeach x) -> toValue (Pair reify x))+      (\(ApplyForeach x) -> toEnc (Pair reify x))+  toJSONKeyList = case toJSONKeyForeach of+    ToJSONKeyTextForall toText toEnc -> ToJSONKeyValue+      (\xs -> toJSON $ map (\(ApplyForeach x) -> toText (Pair reify x)) xs)+      (\xs -> Aeson.list (textEncodingToValueEncoding . toEnc . Pair reify) (map getApplyForeach xs))+    ToJSONKeyValueForall toValue toEnc -> ToJSONKeyValue+      (\xs -> toJSON $ map (\(ApplyForeach x) -> toValue (Pair reify x)) xs)+      (\xs -> Aeson.list (toEnc . Pair reify) (map getApplyForeach xs))++-- this is always safe+textEncodingToValueEncoding :: Aeson.Encoding' Aeson.Key -> Aeson.Encoding' Aeson.Value+textEncodingToValueEncoding = AEI.retagEncoding++instance (FromJSONKeyForeach f, Reify a) => FromJSONKey (ApplyForeach f a) where+  fromJSONKey = case fromJSONKeyForeach of+    FromJSONKeyTextParserForeach f -> FromJSONKeyTextParser (fmap ApplyForeach . f reify . Key.fromText)+    FromJSONKeyValueForeach f -> FromJSONKeyValue (fmap ApplyForeach . f reify)+  fromJSONKeyList = case fromJSONKeyForeach of+    FromJSONKeyTextParserForeach f -> FromJSONKeyValue $ Aeson.withArray "ApplyForeach" $ \xs -> do+      fmap V.toList (mapM (fmap ApplyForeach . Aeson.withText "ApplyForeach" (f reify . Key.fromText)) xs)+    FromJSONKeyValueForeach f -> FromJSONKeyValue $ Aeson.withArray "ApplyForeach" $ \xs -> do+      fmap V.toList (mapM (fmap ApplyForeach . f reify) xs)++class ToJSONKeyForall f where+  toJSONKeyForall :: ToJSONKeyFunctionForall f++class ToJSONKeyForeach f where+  toJSONKeyForeach :: ToJSONKeyFunctionForall (Product Sing f)++class FromJSONKeyExists f where+  fromJSONKeyExists :: FromJSONKeyFunction (Exists f)++class FromJSONKeyForeach f where+  fromJSONKeyForeach :: FromJSONKeyFunctionForeach f++class ToJSONForall f where+  toJSONForall :: f a -> Aeson.Value++class ToJSONForeach f where+  toJSONForeach :: Sing a -> f a -> Aeson.Value++class FromJSONForall f where+  parseJSONForall :: Sing a -> Aeson.Value -> Aeson.Parser (f a)++class FromJSONForeach f where+  parseJSONForeach :: Sing a -> Aeson.Value -> Aeson.Parser (f a)++class FromJSONExists f where+  parseJSONExists :: Aeson.Value -> Aeson.Parser (Exists f)++instance FromJSON a => FromJSONForeach (Const a) where+  parseJSONForeach _ = fmap Const . parseJSON++instance ToJSON a => ToJSONForeach (Const a) where+  toJSONForeach _ = coerce (toJSON @a)++-- I need to get rid of the ToJSONForall and FromJSONForeach constraints+-- on these two instances.+instance (ToJSONKeyForall f, ToJSONForall f) => ToJSONKey (Exists f) where+  toJSONKey = case toJSONKeyForall of+    ToJSONKeyTextForall t e -> ToJSONKeyText (\(Exists a) -> t a) (\(Exists a) -> e a)+    ToJSONKeyValueForall v e -> ToJSONKeyValue (\x -> case x of Exists a -> v a) (\(Exists a) -> e a)++instance (FromJSONKeyExists f, FromJSONExists f) => FromJSONKey (Exists f) where+  fromJSONKey = fromJSONKeyExists++instance ToJSONForall f => ToJSON (Exists f) where+  toJSON (Exists a) = toJSONForall a++instance FromJSONExists f => FromJSON (Exists f) where+  parseJSON v = parseJSONExists v++instance (Aeson.ToJSON1 f, ToJSONForall g) => ToJSONForall (Compose f g) where+  toJSONForall (Compose x) = Aeson.liftToJSON (\_ -> False) toJSONForall (Aeson.toJSON . map toJSONForall) x++instance (Aeson.ToJSON1 f, ToJSONForeach g) => ToJSONForeach (Compose f g) where+  toJSONForeach s (Compose x) = Aeson.liftToJSON (\_ -> False) (toJSONForeach s) (Aeson.toJSON . map (toJSONForeach s)) x++instance (Aeson.FromJSON1 f, FromJSONForeach g) => FromJSONForeach (Compose f g) where+  parseJSONForeach s = fmap Compose . Aeson.liftParseJSON Nothing+    (parseJSONForeach s)+    (Aeson.withArray "Compose" (fmap V.toList . V.mapM (parseJSONForeach s)))++class ToJSONSing k where+  toJSONSing :: forall (a :: k). Sing a -> Aeson.Value++instance (ToJSONForeach f, ToJSONSing k) => ToJSON (Some (f :: k -> Type)) where+  toJSON (Some s v) = toJSON [toJSONSing s, toJSONForeach s v]++class FromJSONSing k where+  parseJSONSing :: Aeson.Value -> Aeson.Parser (Exists (Sing :: k -> Type))++instance (Aeson.FromJSON1 f, FromJSONForall g) => FromJSONForall (Compose f g) where+  parseJSONForall s = fmap Compose . Aeson.liftParseJSON Nothing+      (parseJSONForall s)+          (Aeson.withArray "Compose" (fmap V.toList . V.mapM (parseJSONForall s)))++instance (FromJSONForeach f, FromJSONSing k) => FromJSON (Some (f :: k -> Type)) where+  parseJSON = Aeson.withArray "Some" $ \v -> if V.length v == 2+    then do+      let x = V.unsafeIndex v 0+          y = V.unsafeIndex v 1+      Exists s <- parseJSONSing x :: Aeson.Parser (Exists (Sing :: k -> Type))+      val <- parseJSONForeach s y+      return (Some s val)+    else fail "array of length 2 expected"++-- This name is not great. I need to figure out a better naming+-- scheme that allows this area to grow.+toJSONMapForeachKey :: (ToJSONKeyForeach f, ToJSONForeach v)+  => Sing a+  -> Map (f a) (v a)+  -> Aeson.Value+toJSONMapForeachKey s m = case toJSONKeyForeach of+  ToJSONKeyTextForall keyToText _ -> toJSON $ M.foldlWithKey'+    ( \hm key val -> HM.insert (keyToText (Pair s key)) (toJSONForeach s val) hm+    ) HM.empty m+  ToJSONKeyValueForall keyToValue _ -> toJSON $ M.foldrWithKey' +    ( \key val xs -> (keyToValue (Pair s key), toJSONForeach s val) : xs+    ) [] m++-- | Parse a 'Map' whose key type is higher-kinded. This only creates a valid 'Map'+--   if the 'OrdForeach' instance agrees with the 'Ord' instance.+parseJSONMapForeachKey :: forall k (f :: k -> Type) (a :: k) v. (FromJSONKeyForeach f, OrdForeach f, Unreify k)+  => (Aeson.Value -> Aeson.Parser v)+  -> Sing a+  -> Aeson.Value+  -> Aeson.Parser (Map (f a) v)+parseJSONMapForeachKey valueParser s obj = unreify s $ case fromJSONKeyForeach of+  FromJSONKeyTextParserForeach f -> Aeson.withObject "Map k v"+    ( fmap (M.mapKeysMonotonic getApplyForeach) . KM.foldrWithKey+      (\k v m -> M.insert+        <$> (coerce (f s k :: Aeson.Parser (f a)) :: Aeson.Parser (ApplyForeach f a)) <?> Key k+        <*> valueParser v <?> Key k+        <*> m+      ) (pure M.empty)+    ) obj+  FromJSONKeyValueForeach f -> Aeson.withArray "Map k v"+    ( fmap (M.mapKeysMonotonic getApplyForeach . M.fromList)+    . (coerce :: Aeson.Parser [(f a, v)] -> Aeson.Parser [(ApplyForeach f a, v)])+    . TRV.sequence+    . zipWith (parseIndexedJSONPair (f s) valueParser) [0..]+    . V.toList+    ) obj++-- copied from aeson+parseIndexedJSONPair :: (Aeson.Value -> Aeson.Parser a) -> (Aeson.Value -> Aeson.Parser b) -> Int -> Aeson.Value -> Aeson.Parser (a, b)+parseIndexedJSONPair keyParser valParser idx value = p value <?> Index idx+  where+    p = Aeson.withArray "(k,v)" $ \ab ->+        let n = V.length ab+        in if n == 2+             then (,) <$> parseJSONElemAtIndex keyParser 0 ab+                      <*> parseJSONElemAtIndex valParser 1 ab+             else fail $ "cannot unpack array of length " +++                         show n ++ " into a pair"+{-# INLINE parseIndexedJSONPair #-}++-- copied from aeson+parseJSONElemAtIndex :: (Aeson.Value -> Aeson.Parser a) -> Int -> V.Vector Aeson.Value -> Aeson.Parser a+parseJSONElemAtIndex p idx ary = p (V.unsafeIndex ary idx) <?> Index idx