{-# LANGUAGE DefaultSignatures, TypeFamilies, TypeOperators, PolyKinds, FlexibleInstances, ScopedTypeVariables, FlexibleContexts #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module ProjectM36.Atomable where
import ProjectM36.Base
import ProjectM36.DataTypes.List
import ProjectM36.DataTypes.NonEmptyList
import ProjectM36.DataTypes.Maybe
import ProjectM36.DataTypes.Either
import GHC.Generics
import qualified Data.Map as M
import qualified Data.Text as T
import qualified Data.Vector as V
import Control.DeepSeq (NFData)
import Data.Binary
import Control.Applicative
import Data.Time.Calendar
import Data.ByteString (ByteString)
import Data.Time.Clock
import Data.Proxy
import qualified Data.List.NonEmpty as NE
class (Eq a, NFData a, Binary a, Show a) => Atomable a where
toAtom :: a -> Atom
default toAtom :: (Generic a, AtomableG (Rep a)) => a -> Atom
toAtom v = toAtomG (from v) (toAtomTypeG (from v))
fromAtom :: Atom -> a
default fromAtom :: (Generic a, AtomableG (Rep a)) => Atom -> a
fromAtom v@(ConstructedAtom _ _ args) = case fromAtomG v args of
Nothing -> error "no fromAtomG traversal found"
Just x -> to x
fromAtom v = case fromAtomG v [] of
Nothing -> error "no fromAtomG for Atom found"
Just x -> to x
toAtomType :: proxy a -> AtomType
default toAtomType :: (Generic a, AtomableG (Rep a)) => proxy a -> AtomType
toAtomType _ = toAtomTypeG (from (undefined :: a))
toAddTypeExpr :: proxy a -> DatabaseContextExpr
default toAddTypeExpr :: (Generic a, AtomableG (Rep a)) => proxy a -> DatabaseContextExpr
toAddTypeExpr _ = toAddTypeExprG (from (error "insufficient laziness" :: a)) (toAtomType (Proxy :: Proxy a))
instance Atomable Integer where
toAtom = IntegerAtom
fromAtom (IntegerAtom i) = i
fromAtom e = error ("improper fromAtom" ++ show e)
toAtomType _ = IntegerAtomType
toAddTypeExpr _ = NoOperation
instance Atomable Int where
toAtom = IntAtom
fromAtom (IntAtom i) = i
fromAtom e = error ("improper fromAtom" ++ show e)
toAtomType _ = IntAtomType
toAddTypeExpr _ = NoOperation
instance Atomable Double where
toAtom = DoubleAtom
fromAtom (DoubleAtom d) = d
fromAtom _ = error "improper fromAtom"
toAtomType _ = DoubleAtomType
toAddTypeExpr _ = NoOperation
instance Atomable T.Text where
toAtom = TextAtom
fromAtom (TextAtom t) = t
fromAtom _ = error "improper fromAtom"
toAtomType _ = TextAtomType
toAddTypeExpr _ = NoOperation
instance Atomable Day where
toAtom = DayAtom
fromAtom (DayAtom d) = d
fromAtom _ = error "improper fromAtom"
toAtomType _ = DayAtomType
toAddTypeExpr _ = NoOperation
instance Atomable UTCTime where
toAtom = DateTimeAtom
fromAtom (DateTimeAtom t) = t
fromAtom _ = error "improper fromAtom"
toAtomType _ = DateTimeAtomType
toAddTypeExpr _ = NoOperation
instance Atomable ByteString where
toAtom = ByteStringAtom
fromAtom (ByteStringAtom b) = b
fromAtom _ = error "improper fromAtom"
toAtomType _ = ByteStringAtomType
toAddTypeExpr _ = NoOperation
instance Atomable Bool where
toAtom = BoolAtom
fromAtom (BoolAtom b) = b
fromAtom _ = error "improper fromAtom"
toAtomType _ = BoolAtomType
toAddTypeExpr _ = NoOperation
instance Atomable a => Atomable (Maybe a) where
toAtom (Just v) = ConstructedAtom "Just" (maybeAtomType (toAtomType (Proxy :: Proxy a))) [toAtom v]
toAtom Nothing = ConstructedAtom "Nothing" (maybeAtomType (toAtomType (Proxy :: Proxy a))) []
fromAtom (ConstructedAtom "Just" _ [val]) = Just (fromAtom val)
fromAtom (ConstructedAtom "Nothing" _ []) = Nothing
fromAtom _ = error "improper fromAtom (Maybe a)"
toAtomType _ = ConstructedAtomType "Maybe" (M.singleton "a" (toAtomType (Proxy :: Proxy a)))
toAddTypeExpr _ = NoOperation
instance (Atomable a, Atomable b) => Atomable (Either a b) where
toAtom (Left l) = ConstructedAtom "Left" (eitherAtomType (toAtomType (Proxy :: Proxy a))
(toAtomType (Proxy :: Proxy b))) [toAtom l]
toAtom (Right r) = ConstructedAtom "Right" (eitherAtomType (toAtomType (Proxy :: Proxy a))
(toAtomType (Proxy :: Proxy b))) [toAtom r]
fromAtom (ConstructedAtom "Left" _ [val]) = Left (fromAtom val)
fromAtom (ConstructedAtom "Right" _ [val]) = Right (fromAtom val)
fromAtom _ = error "improper fromAtom (Either a b)"
instance Atomable a => Atomable [a] where
toAtom [] = ConstructedAtom "Empty" (listAtomType (toAtomType (Proxy :: Proxy a))) []
toAtom (x:xs) = ConstructedAtom "Cons" (listAtomType (toAtomType (Proxy :: Proxy a))) (toAtom x: [toAtom xs])
fromAtom (ConstructedAtom "Empty" _ _) = []
fromAtom (ConstructedAtom "Cons" _ [x,y]) = fromAtom x:fromAtom y
fromAtom _ = error "improper fromAtom [a]"
toAtomType _ = ConstructedAtomType "List" (M.singleton "a" (toAtomType (Proxy :: Proxy a)))
toAddTypeExpr _ = NoOperation
instance Atomable a => Atomable (NE.NonEmpty a) where
toAtom (x NE.:| []) = ConstructedAtom "NECons" (nonEmptyListAtomType (toAtomType (Proxy :: Proxy a))) [toAtom x]
toAtom (x NE.:| xs) = ConstructedAtom "NECons" (nonEmptyListAtomType (toAtomType (Proxy :: Proxy a))) (map toAtom (x:xs))
fromAtom _ = error "improper fromAtom (NonEmptyList a)"
toAtomType _ = ConstructedAtomType "NonEmptyList" (M.singleton "a" (toAtomType (Proxy :: Proxy a)))
toAddTypeExpr _ = NoOperation
#if !MIN_VERSION_binary(0,8,4)
instance Binary a => Binary (NE.NonEmpty a)
#endif
class AtomableG g where
toAtomG :: g a -> AtomType -> Atom
fromAtomG :: Atom -> [Atom] -> Maybe (g a)
toAtomTypeG :: g a -> AtomType
toAtomsG :: g a -> [Atom]
toAddTypeExprG :: g a -> AtomType -> DatabaseContextExpr
getConstructorsG :: g a -> [DataConstructorDef]
getConstructorArgsG :: g a -> [DataConstructorDefArg]
instance (Datatype c, AtomableG a) => AtomableG (M1 D c a) where
toAtomG (M1 v) = toAtomG v
fromAtomG atom args = M1 <$> fromAtomG atom args
toAtomsG = error "invalid toAtomsG in M1 D"
toAtomTypeG _ = ConstructedAtomType (T.pack typeName) M.empty
where
typeName = datatypeName (undefined :: M1 D c a x)
toAddTypeExprG ~(M1 v) (ConstructedAtomType tcName _) = AddTypeConstructor tcDef dataConstructors
where
tcDef = ADTypeConstructorDef tcName []
dataConstructors = getConstructorsG v
toAddTypeExprG _ _ = NoOperation
getConstructorsG ~(M1 v) = getConstructorsG v
getConstructorArgsG = error "invalid getConstructorArgsG in M1 D"
instance (Constructor c, AtomableG a) => AtomableG (M1 C c a) where
toAtomG (M1 v) t = ConstructedAtom (T.pack constructorName) t atoms
where
atoms = toAtomsG v
constructorName = conName (undefined :: M1 C c a x)
fromAtomG atom@(ConstructedAtom dConsName _ _) args = if dName == dConsName then
M1 <$> fromAtomG atom args
else
Nothing
where
dName = T.pack (conName (undefined :: M1 C c a x))
fromAtomG _ _ = error "invalid fromAtomG in M1 C"
toAtomsG = error "invalid toAtomsG in M1 C"
toAtomTypeG = error "invalid toAtomTypeG in M1 C"
toAddTypeExprG = error "invalid toAddTypeExprG in M1 C"
getConstructorsG ~(M1 v) = [DataConstructorDef (T.pack dName) dArgs]
where
dName = conName (undefined :: M1 C c a x)
dArgs = getConstructorArgsG v
getConstructorArgsG = undefined
instance (Selector c, AtomableG a) => AtomableG (M1 S c a) where
toAtomG = undefined
fromAtomG atom args = M1 <$> fromAtomG atom args
toAtomsG (M1 v) = toAtomsG v
toAtomTypeG (M1 v) = toAtomTypeG v
toAddTypeExprG = error "invalid toAddTypeExprG in M1 S"
getConstructorsG = error "invalid getConstructorsG in M1 S"
getConstructorArgsG ~(M1 v) = getConstructorArgsG v
instance (Atomable a) => AtomableG (K1 c a) where
toAtomG (K1 v) _ = toAtom v
fromAtomG _ args = K1 <$> Just (fromAtom (headatom args))
where headatom (x:_) = x
headatom [] = error "no more atoms for constructor!"
toAtomsG (K1 v) = [toAtom v]
toAtomTypeG _ = toAtomType (Proxy :: Proxy a)
toAddTypeExprG = error "invalid toAddTypeExprG in K1"
getConstructorsG = error "invalid getConstructorsG in K1"
getConstructorArgsG ~(K1 _) = [DataConstructorDefTypeConstructorArg tCons]
where
tCons = typeToTypeConstructor $ toAtomType (Proxy :: Proxy a)
typeToTypeConstructor :: AtomType -> TypeConstructor
typeToTypeConstructor x@IntAtomType = PrimitiveTypeConstructor "Int" x
typeToTypeConstructor x@IntegerAtomType = PrimitiveTypeConstructor "Integer" x
typeToTypeConstructor x@DoubleAtomType = PrimitiveTypeConstructor "Double" x
typeToTypeConstructor x@TextAtomType = PrimitiveTypeConstructor "Text" x
typeToTypeConstructor x@DayAtomType = PrimitiveTypeConstructor "Day" x
typeToTypeConstructor x@DateTimeAtomType = PrimitiveTypeConstructor "DateTime" x
typeToTypeConstructor x@ByteStringAtomType = PrimitiveTypeConstructor "ByteString" x
typeToTypeConstructor x@BoolAtomType = PrimitiveTypeConstructor "Bool" x
typeToTypeConstructor (RelationAtomType attrs)
= RelationAtomTypeConstructor $ map attrToAttrExpr $ V.toList attrs
where
attrToAttrExpr (Attribute n t) = AttributeAndTypeNameExpr n (typeToTypeConstructor t) ()
typeToTypeConstructor (ConstructedAtomType tcName tvMap)
= ADTypeConstructor tcName $ map typeToTypeConstructor (M.elems tvMap)
typeToTypeConstructor (TypeVariableType tvName) = TypeVariable tvName
instance AtomableG U1 where
toAtomG = error "invalid toAtomG in U1"
fromAtomG _ _ = pure U1
toAtomsG _ = []
toAtomTypeG = error "invalid toAtomTypeG in U1"
toAddTypeExprG = error "invalid toAddTypeExprG in U1"
getConstructorsG = error "invalid getConstructorsG in U1"
getConstructorArgsG _ = []
instance (AtomableG a, AtomableG b) => AtomableG (a :*: b) where
toAtomG = undefined
fromAtomG atom args = (:*:) <$> fromAtomG atom splitargs1 <*> fromAtomG atom splitargs2
where splitargs1 = take splitpoint args
splitargs2 = drop splitpoint args
splitpoint = length args `div` 2
toAtomTypeG = error "invalid toAtomTypeG in :*:"
toAtomsG (x :*: y) = toAtomsG x ++ toAtomsG y
toAddTypeExprG = error "invalid toAddTypeExprG in :*:"
getConstructorsG = error "invalid getConstructorsG in :*:"
getConstructorArgsG ~(x :*: y) = getConstructorArgsG x ++ getConstructorArgsG y
instance (AtomableG a, AtomableG b) => AtomableG (a :+: b) where
toAtomG (L1 x) = toAtomG x
toAtomG (R1 x) = toAtomG x
fromAtomG atom args = (L1 <$> fromAtomG atom args) <|> (R1 <$> fromAtomG atom args)
toAtomTypeG = error "invalid toAtomTypeG in :+:"
toAtomsG (L1 x) = toAtomsG x
toAtomsG (R1 x) = toAtomsG x
toAddTypeExprG = error "invalid toAddTypeExprG in :+:"
getConstructorsG _ = getConstructorsG (undefined :: a x) ++ getConstructorsG (undefined :: b x)
getConstructorArgsG = error "invalid getConstructorArgsG in :+:"