{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} -- {-# LANGUAGE PartialTypeSignatures #-} module Data.TypedEncoding.Internal.Class where import Data.TypedEncoding.Internal.Types (Enc(..) , toEncoding , getPayload , withUnsafeCoerce , unsafeChangePayload , RecreateEx(..) , UnexpectedDecodeEx(..)) import Data.Proxy import Data.Functor.Identity import GHC.TypeLits import Data.Semigroup ((<>)) import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy.Char8 as BL import qualified Data.Text as T import qualified Data.Text.Lazy as TL import qualified Data.List as L -- $setup -- >>> :set -XScopedTypeVariables -XKindSignatures -XMultiParamTypeClasses -XDataKinds -XPolyKinds -XFlexibleInstances -XFlexibleContexts -- >>> import Data.TypedEncoding.Internal.Types (unsafeSetPayload) class EncodeF f instr outstr where encodeF :: instr -> f outstr class EncodeFAll f (xs :: [k]) c str where encodeFAll :: (Enc '[] c str) -> f (Enc xs c str) instance Applicative f => EncodeFAll f '[] c str where encodeFAll (MkEnc _ c str) = pure $ toEncoding c str instance (Monad f, EncodeFAll f xs c str, EncodeF f (Enc xs c str) (Enc (x ': xs) c str)) => EncodeFAll f (x ': xs) c str where encodeFAll str = let re :: f (Enc xs c str) = encodeFAll str in re >>= encodeF encodeAll :: EncodeFAll Identity (xs :: [k]) c str => (Enc '[] c str) -> (Enc xs c str) encodeAll = runIdentity . encodeFAll class DecodeF f instr outstr where decodeF :: instr -> f outstr class DecodeFAll f (xs :: [k]) c str where decodeFAll :: (Enc xs c str) -> f (Enc '[] c str) instance Applicative f => DecodeFAll f '[] c str where decodeFAll (MkEnc _ c str) = pure $ toEncoding c str instance (Monad f, DecodeFAll f xs c str, DecodeF f (Enc (x ': xs) c str) (Enc (xs) c str)) => DecodeFAll f (x ': xs) c str where decodeFAll str = let re :: f (Enc xs c str) = decodeF str in re >>= decodeFAll decodeAll :: DecodeFAll Identity (xs :: [k]) c str => (Enc xs c str) -> (Enc '[] c str) decodeAll = runIdentity . decodeFAll -- | Used to safely recover encoded data validating all encodingss class RecreateF f instr outstr where checkPrevF :: outstr -> f instr class (Functor f) => RecreateFAll f (xs :: [k]) c str where checkFAll :: (Enc xs c str) -> f (Enc '[] c str) recreateFAll :: (Enc '[] c str) -> f (Enc xs c str) recreateFAll str@(MkEnc _ _ pay) = let str0 :: Enc xs c str = withUnsafeCoerce id str in fmap (withUnsafeCoerce (const pay)) $ checkFAll str0 instance Applicative f => RecreateFAll f '[] c str where checkFAll (MkEnc _ c str) = pure $ toEncoding c str instance (Monad f, RecreateFAll f xs c str, RecreateF f (Enc xs c str) (Enc (x ': xs) c str)) => RecreateFAll f (x ': xs) c str where checkFAll str = let re :: f (Enc xs c str) = checkPrevF str in re >>= checkFAll recreateAll :: RecreateFAll Identity (xs :: [k]) c str => (Enc '[] c str) -> (Enc xs c str) recreateAll = runIdentity . recreateFAll -- | TODO use singletons definition instead? type family Append (xs :: [k]) (ys :: [k]) :: [k] where Append '[] xs = xs Append (y ': ys) xs = y ': (Append ys xs) encodeFPart :: forall f xs xsf c str . (Functor f, EncodeFAll f xs c str) => Proxy xs -> (Enc xsf c str) -> f (Enc (Append xs xsf) c str) encodeFPart p (MkEnc _ conf str) = let re :: f (Enc xs c str) = encodeFAll $ MkEnc Proxy conf str in (MkEnc Proxy conf . getPayload) <$> re encodePart :: EncodeFAll Identity (xs :: [k]) c str => Proxy xs -> (Enc xsf c str) -> (Enc (Append xs xsf) c str) encodePart p = runIdentity . encodeFPart p -- | Unsafe implementation guarded by safe type definition decodeFPart :: forall f xs xsf c str . (Functor f, DecodeFAll f xs c str) => Proxy xs -> (Enc (Append xs xsf) c str) -> f (Enc xsf c str) decodeFPart p (MkEnc _ conf str) = let re :: f (Enc '[] c str) = decodeFAll $ MkEnc (Proxy :: Proxy xs) conf str in (MkEnc Proxy conf . getPayload) <$> re decodePart :: DecodeFAll Identity (xs :: [k]) c str => Proxy xs -> (Enc (Append xs xsf) c str) -> (Enc xsf c str) decodePart p = runIdentity . decodeFPart p -- Other classes -- -- subsets are useful for restriction encodings -- like r-UFT8 but not for other encodings. class Subset (x :: k) (y :: k) where inject :: Proxy y -> Enc (x ': xs) c str -> Enc (y ': xs) c str inject _ = withUnsafeCoerce id class FlattenAs (x :: k) (y :: k) where flattenAs :: Proxy y -> Enc (x ': xs) c str -> Enc '[y] c str flattenAs _ = withUnsafeCoerce id -- | Polymorphic data payloads used to encode/decode class HasA c a where has :: Proxy a -> c -> a instance HasA a () where has _ = const () -- | With type safety in pace decoding errors should be unexpected -- this class can be used to provide extra info if decoding could fail class UnexpectedDecodeErr f where unexpectedDecodeErr :: UnexpectedDecodeEx -> f a instance UnexpectedDecodeErr Identity where unexpectedDecodeErr x = fail $ show x instance UnexpectedDecodeErr (Either UnexpectedDecodeEx) where unexpectedDecodeErr = Left asUnexpected :: (UnexpectedDecodeErr f, Applicative f, Show err, KnownSymbol x) => Proxy x -> Either err a -> f a asUnexpected p (Left err) = unexpectedDecodeErr $ UnexpectedDecodeEx p err asUnexpected _ (Right r) = pure r -- TODO using RecreateErr typeclass is overkill -- | Recovery errors are expected unless Recovery allows Identity instance class RecreateErr f where recoveryErr :: RecreateEx -> f a instance RecreateErr (Either RecreateEx) where recoveryErr = Left asRecreateErr :: (RecreateErr f, Applicative f, Show err, KnownSymbol x) => Proxy x -> Either err a -> f a asRecreateErr p (Left err) = recoveryErr $ RecreateEx p err asRecreateErr _ (Right r) = pure r -- * Display -- | Human friendly version of Show class Displ x where displ :: x -> String instance Displ String where displ = id instance Displ T.Text where displ x = "(Text " ++ T.unpack x ++ ")" instance Displ TL.Text where displ x = "(TL.Text " ++ TL.unpack x ++ ")" instance Displ B.ByteString where displ x = "(ByteString " ++ B.unpack x ++ ")" instance Displ BL.ByteString where displ x = "(ByteString " ++ BL.unpack x ++ ")" instance Displ (Proxy '[]) where displ _ = "" -- | -- >>> displ (Proxy :: Proxy ["FIRST", "SECOND"]) -- "FIRST,SECOND" instance (pxs ~ Proxy xs, Displ pxs, KnownSymbol x) => Displ (Proxy (x ': xs)) where displ _ = L.dropWhileEnd (',' ==) $ symbolVal (Proxy :: Proxy x) ++ "," ++ displ (Proxy :: Proxy xs) -- >>> let disptest = unsafeSetPayload () "hello" :: Enc '["TEST"] () T.Text -- >>> displ disptest -- "MkEnc '[TEST] () hello" instance (Displ (Proxy xs), Show c, Displ str) => Displ ( Enc xs c str) where displ (MkEnc p c s) = "MkEnc '[" ++ displ p ++ "] " ++ show c ++ " " ++ displ s -- Utils -- errorOnLeft :: Show err => Either err a -> a errorOnLeft (Left e) = error $ "You trusted encodings too much " <> show e errorOnLeft (Right r) = r