{-# LANGUAGE CPP #-} {-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE Trustworthy #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} #if __GLASGOW_HASKELL__ >= 711 {-# LANGUAGE PatternSynonyms #-} #endif #if __GLASGOW_HASKELL__ >= 800 {-# LANGUAGE TypeInType #-} #endif {-# OPTIONS_HADDOCK not-home #-} module Data.Structured.Internal where import Data.Structured.MD5 import Data.Int (Int16, Int32, Int64, Int8) import Data.List.NonEmpty (NonEmpty) import Data.Proxy (Proxy (..)) import Data.Ratio (Ratio) import Data.Tagged (Tagged (..), untag) import Data.Word (Word, Word16, Word32, Word64, Word8) import Numeric.Natural (Natural) import qualified Control.Monad.Trans.State.Strict as State import GHC.Generics import qualified Data.Aeson as Aeson import qualified Data.Array.IArray as Array import qualified Data.Array.Unboxed as Array import qualified Data.ByteString as BS import qualified Data.ByteString.Builder as Builder import qualified Data.ByteString.Lazy as LBS import qualified Data.ByteString.Short as SBS import qualified Data.Fixed as Fixed import qualified Data.HashMap.Lazy as HML import qualified Data.HashSet as HS import qualified Data.IntMap as IM import qualified Data.IntSet as IS import qualified Data.Map as Map import qualified Data.Monoid as Monoid import qualified Data.Scientific as Sci import qualified Data.Semigroup as Semigroup import qualified Data.Sequence as Seq import qualified Data.Set as Set import qualified Data.Text as T import qualified Data.Text.Lazy as LT import qualified Data.Time.Compat as Time import qualified Data.UUID.Types as UUID import qualified Data.Vector as V import qualified Data.Vector.Storable as SV import qualified Data.Vector.Unboxed as UV import qualified Data.Version as Version #if MIN_VERSION_aeson(2,0,0) import qualified Data.Aeson.Key as Key import qualified Data.Aeson.KeyMap as KM #endif #if __GLASGOW_HASKELL__ >= 800 import Data.Kind (Type) #else #define Type * #endif import Data.Typeable (TypeRep, Typeable, typeRep) import Data.Monoid (mconcat) import qualified Data.Foldable import qualified Data.Semigroup #if !MIN_VERSION_base(4,8,0) import Control.Applicative (pure) import Data.Traversable (traverse) #endif #if !MIN_VERSION_base(4,7,0) import Data.Typeable (Typeable1, typeOf1) #endif ------------------------------------------------------------------------------- -- Types ------------------------------------------------------------------------------- type TypeName = String type ConstructorName = String -- | A sematic version of a data type. Usually 0. type TypeVersion = Word32 -- | Structure of a datatype. -- -- It can be infinite, as far as 'TypeRep's involved are finite. -- (e.g. polymorphic recursion might cause troubles). -- data Structure = Nominal !TypeRep !TypeVersion TypeName [Structure] -- ^ nominal, yet can be parametrised by other structures. | Newtype !TypeRep !TypeVersion TypeName Structure -- ^ a newtype wrapper | Structure !TypeRep !TypeVersion TypeName SopStructure -- ^ sum-of-products structure deriving (Eq, Ord, Show, Generic) type SopStructure = [(ConstructorName, [Structure])] -- | A MD5 hash digest of 'Structure'. hashStructure :: Structure -> MD5 hashStructure = md5 . LBS.toStrict . Builder.toLazyByteString . structureBuilder -- | A van-Laarhoven lens into 'TypeVersion' of 'Structure' -- -- @ -- 'typeVersion' :: Lens' 'Structure' 'TypeVersion' -- @ typeVersion :: Functor f => (TypeVersion -> f TypeVersion) -> Structure -> f Structure typeVersion f (Nominal t v n s) = fmap (\v' -> Nominal t v' n s) (f v) typeVersion f (Newtype t v n s) = fmap (\v' -> Newtype t v' n s) (f v) typeVersion f (Structure t v n s) = fmap (\v' -> Structure t v' n s) (f v) -- | A van-Laarhoven lens into 'TypeName' of 'Structure' -- -- @ -- 'typeName' :: Lens' 'Structure' 'TypeName' -- @ typeName :: Functor f => (TypeName -> f TypeName) -> Structure -> f Structure typeName f (Nominal t v n s) = fmap (\n' -> Nominal t v n' s) (f n) typeName f (Newtype t v n s) = fmap (\n' -> Newtype t v n' s) (f n) typeName f (Structure t v n s) = fmap (\n' -> Structure t v n' s) (f n) ------------------------------------------------------------------------------- -- Builder ------------------------------------------------------------------------------- -- | Flatten 'Structure' into something we can calculate hash of. -- -- As 'Structure' can be potentially infinite. For mutually recursive types, -- we keep track of 'TypeRep's, and put just 'TypeRep' name when it's occurred -- another time. structureBuilder :: Structure -> Builder.Builder structureBuilder s0 = State.evalState (go s0) Map.empty where go :: Structure -> State.State (Map.Map String (NonEmpty TypeRep)) Builder.Builder go (Nominal t v n s) = withTypeRep t $ do s' <- traverse go s return $ mconcat $ Builder.word8 1 : Builder.word32LE v : Builder.stringUtf8 n : s' go (Newtype t v n s) = withTypeRep t $ do s' <- go s return $ mconcat [Builder.word8 2, Builder.word32LE v, Builder.stringUtf8 n, s'] go (Structure t v n s) = withTypeRep t $ do s' <- goSop s return $ mconcat [Builder.word8 3, Builder.word32LE v, Builder.stringUtf8 n, s'] withTypeRep t k = do acc <- State.get case insert t acc of Nothing -> return $ mconcat [ Builder.word8 0, Builder.stringUtf8 (show t) ] Just acc' -> do State.put acc' k goSop :: SopStructure -> State.State (Map.Map String (NonEmpty TypeRep)) Builder.Builder goSop sop = do parts <- traverse part sop return $ mconcat parts part (cn, s) = do s' <- traverse go s return $ Data.Monoid.mconcat [ Builder.stringUtf8 cn, mconcat s' ] insert :: TypeRep -> Map.Map String (NonEmpty TypeRep) -> Maybe (Map.Map String (NonEmpty TypeRep)) insert tr m = case Map.lookup trShown m of Nothing -> inserted Just ne | tr `Data.Foldable.elem` ne -> Nothing | otherwise -> inserted where inserted = Just (Map.insertWith (Data.Semigroup.<>) trShown (pure tr) m) trShown = show tr ------------------------------------------------------------------------------- -- Classes ------------------------------------------------------------------------------- -- | Class of types with a known 'Structure'. -- -- For regular data types 'Structured' can be derived generically. -- -- @ -- data Record = Record { a :: Int, b :: Bool, c :: [Char] } deriving ('Generic') -- instance 'Structured' Record -- @ -- -- @since 3.2.0.0 -- class Typeable a => Structured a where structure :: Proxy a -> Structure default structure :: (Generic a, GStructured (Rep a)) => Proxy a -> Structure structure = genericStructure -- This member is hidden. It's there to precalc structureHash' :: Tagged a MD5 structureHash' = Tagged (hashStructure (structure (Proxy :: Proxy a))) -- | Semantically @'hashStructure' . 'structure'@. structureHash :: forall a. Structured a => Proxy a -> MD5 structureHash _ = untag (structureHash' :: Tagged a MD5) ------------------------------------------------------------------------------- -- Smart constructors ------------------------------------------------------------------------------- -- | Use 'Typeable' to infer name nominalStructure :: Typeable a => Proxy a -> Structure nominalStructure p = Nominal tr 0 (show tr) [] where tr = typeRep p #if MIN_VERSION_base(4,7,0) containerStructure :: forall f a. (Typeable f, Structured a) => Proxy (f a) -> Structure containerStructure _ = Nominal faTypeRep 0 (show fTypeRep) [ structure (Proxy :: Proxy a) ] where fTypeRep = typeRep (Proxy :: Proxy f) faTypeRep = typeRep (Proxy :: Proxy (f a)) #else containerStructure :: forall f a. (Typeable1 f, Structured a) => Proxy (f a) -> Structure containerStructure _ = Nominal faTypeRep 0 (show fTypeRep) [ structure (Proxy :: Proxy a) ] where fTypeRep = typeOf1 (undefined :: f ()) faTypeRep = typeRep (Proxy :: Proxy (f a)) #endif ------------------------------------------------------------------------------- -- Generic ------------------------------------------------------------------------------- -- | Derive 'structure' genrically. genericStructure :: forall a. (Typeable a, Generic a, GStructured (Rep a)) => Proxy a -> Structure genericStructure _ = gstructured (typeRep (Proxy :: Proxy a)) (Proxy :: Proxy (Rep a)) 0 -- | Used to implement 'genericStructure'. class GStructured (f :: Type -> Type) where gstructured :: TypeRep -> Proxy f -> TypeVersion -> Structure instance (i ~ D, Datatype c, GStructuredSum f) => GStructured (M1 i c f) where gstructured tr _ v = case sop of #if MIN_VERSION_base(4,7,0) [(_, [s])] | isNewtype p -> Newtype tr v name s #endif _ -> Structure tr v name sop where p = undefined :: M1 i c f () name = datatypeName p sop = gstructuredSum (Proxy :: Proxy f) [] class GStructuredSum (f :: Type -> Type) where gstructuredSum :: Proxy f -> SopStructure -> SopStructure instance (i ~ C, Constructor c, GStructuredProd f) => GStructuredSum (M1 i c f) where gstructuredSum _ xs = (name, prod) : xs where name = conName (undefined :: M1 i c f ()) prod = gstructuredProd (Proxy :: Proxy f) [] instance (GStructuredSum f, GStructuredSum g) => GStructuredSum (f :+: g) where gstructuredSum _ xs = gstructuredSum (Proxy :: Proxy f) $ gstructuredSum (Proxy :: Proxy g) xs instance GStructuredSum V1 where gstructuredSum _ = id class GStructuredProd (f :: Type -> Type) where gstructuredProd :: Proxy f -> [Structure] -> [Structure] instance (i ~ S, GStructuredProd f) => GStructuredProd (M1 i c f) where gstructuredProd _ = gstructuredProd (Proxy :: Proxy f) instance Structured c => GStructuredProd (K1 i c) where gstructuredProd _ xs = structure (Proxy :: Proxy c) : xs instance GStructuredProd U1 where gstructuredProd _ = id instance (GStructuredProd f, GStructuredProd g) => GStructuredProd (f :*: g) where gstructuredProd _ xs = gstructuredProd (Proxy :: Proxy f) $ gstructuredProd (Proxy :: Proxy g) xs ------------------------------------------------------------------------------- -- base ------------------------------------------------------------------------------- instance Structured () instance Structured Bool instance Structured Ordering instance Structured Char where structure = nominalStructure instance (Structured a, Structured b) => Structured (a -> b) where structure _ = Nominal (typeRep (Proxy :: Proxy (a -> b))) 0 ("(->)") [structure (Proxy :: Proxy a), structure (Proxy :: Proxy b)] instance Structured a => Structured (Maybe a) instance (Structured a, Structured b) => Structured (Either a b) instance Structured a => Structured (Ratio a) where structure = containerStructure instance Structured a => Structured [a] where structure = containerStructure instance Structured a => Structured (NonEmpty a) where structure = containerStructure instance (Structured a1, Structured a2) => Structured (a1, a2) instance (Structured a1, Structured a2, Structured a3) => Structured (a1, a2, a3) instance (Structured a1, Structured a2, Structured a3, Structured a4) => Structured (a1, a2, a3, a4) instance (Structured a1, Structured a2, Structured a3, Structured a4, Structured a5) => Structured (a1, a2, a3, a4, a5) instance (Structured a1, Structured a2, Structured a3, Structured a4, Structured a5, Structured a6) => Structured (a1, a2, a3, a4, a5, a6) instance (Structured a1, Structured a2, Structured a3, Structured a4, Structured a5, Structured a6, Structured a7) => Structured (a1, a2, a3, a4, a5, a6, a7) ------------------------------------------------------------------------------- -- base: numbers ------------------------------------------------------------------------------- instance Structured Int where structure = nominalStructure instance Structured Integer where structure = nominalStructure instance Structured Data.Word.Word where structure = nominalStructure instance Structured Int8 where structure = nominalStructure instance Structured Int16 where structure = nominalStructure instance Structured Int32 where structure = nominalStructure instance Structured Int64 where structure = nominalStructure instance Structured Word8 where structure = nominalStructure instance Structured Word16 where structure = nominalStructure instance Structured Word32 where structure = nominalStructure instance Structured Word64 where structure = nominalStructure instance Structured Float where structure = nominalStructure instance Structured Double where structure = nominalStructure #if __GLASGOW_HASKELL__ >=810 instance (Typeable k, Typeable (a :: k), Fixed.HasResolution a) => Structured (Fixed.Fixed a) where #else instance (Typeable a, Fixed.HasResolution a) => Structured (Fixed.Fixed a) where #endif structure _ = Nominal (typeRep (Proxy :: Proxy (Fixed.Fixed a))) 0 ("Fixed " ++ show (Fixed.resolution (Proxy :: Proxy a))) [] instance Structured Natural where structure = nominalStructure ------------------------------------------------------------------------------- -- semigroup ------------------------------------------------------------------------------- instance Structured a => Structured (Semigroup.Min a) instance Structured a => Structured (Semigroup.Max a) instance Structured a => Structured (Semigroup.First a) instance Structured a => Structured (Semigroup.Last a) instance Structured a => Structured (Semigroup.WrappedMonoid a) ------------------------------------------------------------------------------- -- monoid ------------------------------------------------------------------------------- instance Structured a => Structured (Monoid.First a) instance Structured a => Structured (Monoid.Last a) instance Structured a => Structured (Monoid.Sum a) instance Structured a => Structured (Monoid.Product a) instance Structured a => Structured (Monoid.Dual a) instance Structured a => Structured (Monoid.Endo a) instance Structured Monoid.All instance Structured Monoid.Any ------------------------------------------------------------------------------- -- bytestring ------------------------------------------------------------------------------- instance Structured BS.ByteString where structure = nominalStructure instance Structured LBS.ByteString where structure = nominalStructure instance Structured SBS.ShortByteString where structure = nominalStructure ------------------------------------------------------------------------------- -- text ------------------------------------------------------------------------------- instance Structured T.Text where structure = nominalStructure instance Structured LT.Text where structure = nominalStructure ------------------------------------------------------------------------------- -- containers ------------------------------------------------------------------------------- instance (Structured k, Structured v) => Structured (Map.Map k v) where structure _ = Nominal (typeRep (Proxy :: Proxy (Map.Map k v))) 0 "Map" [ structure (Proxy :: Proxy k), structure (Proxy :: Proxy v) ] instance (Structured k) => Structured (Set.Set k) where structure = containerStructure instance (Structured v) => Structured (IM.IntMap v) where structure = containerStructure instance Structured IS.IntSet where structure = nominalStructure instance (Structured v) => Structured (Seq.Seq v) where structure = containerStructure ------------------------------------------------------------------------------- -- time ------------------------------------------------------------------------------- instance Structured Time.UTCTime where structure = nominalStructure instance Structured Time.DiffTime where structure = nominalStructure instance Structured Time.UniversalTime where structure = nominalStructure instance Structured Time.NominalDiffTime where structure = nominalStructure instance Structured Time.Day where structure = nominalStructure instance Structured Time.TimeZone where structure = nominalStructure instance Structured Time.TimeOfDay where structure = nominalStructure instance Structured Time.LocalTime where structure = nominalStructure instance Structured Time.DayOfWeek where structure = nominalStructure ------------------------------------------------------------------------------- -- array ------------------------------------------------------------------------------- instance (Structured i, Structured e) => Structured (Array.Array i e) where structure _ = Nominal (typeRep (Proxy :: Proxy (Array.Array i e))) 0 "Array" [ structure (Proxy :: Proxy i), structure (Proxy :: Proxy e) ] instance (Structured i, Structured e) => Structured (Array.UArray i e) where structure _ = Nominal (typeRep (Proxy :: Proxy (Array.UArray i e))) 0 "UArray" [ structure (Proxy :: Proxy i), structure (Proxy :: Proxy e) ] ------------------------------------------------------------------------------- -- aeson ------------------------------------------------------------------------------- instance Structured Aeson.Value #if MIN_VERSION_aeson(2,0,0) instance Structured Key.Key where structure = nominalStructure instance Structured v => Structured (KM.KeyMap v) where structure = containerStructure #endif ------------------------------------------------------------------------------- -- unordered-containers ------------------------------------------------------------------------------- instance (Structured k, Structured v) => Structured (HML.HashMap k v) where structure _ = Nominal (typeRep (Proxy :: Proxy (HML.HashMap v))) 0 "HashMap" [ structure (Proxy :: Proxy k), structure (Proxy :: Proxy v) ] instance (Structured k) => Structured (HS.HashSet k) where structure = containerStructure ------------------------------------------------------------------------------- -- vector ------------------------------------------------------------------------------- instance (Structured v) => Structured (V.Vector v) where structure = containerStructure instance (Structured v) => Structured (SV.Vector v) where structure = containerStructure instance (Structured v) => Structured (UV.Vector v) where structure = containerStructure ------------------------------------------------------------------------------- -- scientific ------------------------------------------------------------------------------- instance Structured Sci.Scientific where structure = nominalStructure ------------------------------------------------------------------------------- -- uuid-types ------------------------------------------------------------------------------- instance Structured UUID.UUID where structure = nominalStructure ------------------------------------------------------------------------------- -- base: version ------------------------------------------------------------------------------- -- Generic Version is since base-4.9.0.0 instance Structured Version.Version where structure = nominalStructure ------------------------------------------------------------------------------- -- tagged ------------------------------------------------------------------------------- #if __GLASGOW_HASKELL__ >= 800 instance (Typeable k, Typeable (b :: k), Structured a) => Structured (Tagged b a) #else instance (Typeable (b :: Type), Structured a) => Structured (Tagged b a) #endif -- Proxy isn't Typeable in base-4.8 / base -- #if __GLASGOW_HASKELL__ >= 800 -- instance (Typeable k, Typeable (a :: k)) => Structured (Proxy a) -- #else -- instance (Typeable a) => Structured (Proxy a) where -- structure p = Structure (typeRep p) 0 "Proxy" [("Proxy",[])] -- #endif