@@ -56,22 +56,30 @@ instance Atomable Relation
5656instance (Atomable a) => Atomable (Maybe a)
5757
5858-- | Database atoms are the smallest, undecomposable units of a tuple. Common examples are integers, text, or unique identity keys.
59−data Atom = forall a. Atomable a => Atom a
60−
59+data Atom = forall a. Atomable a => Atom a |
60+ ConstructedAtom DataConstructorName AtomType [Atom]
61+
6162instance Eq Atom where
6263 (Atom a) == (Atom b) = Just a == cast b
64+ (ConstructedAtom dConsNameA aTypeA atomListA) == (ConstructedAtom dConsNameB aTypeB atomListB) = aTypeA == aTypeB &&
65+ atomListA == atomListB &&
66+ dConsNameA == dConsNameB
67+ _ == _ = False
6368
6469instance Show Atom where
6570 show (Atom atom) = "Atom " ++ show atom
71+ show (ConstructedAtom dConsName _ atomList) = unpack dConsName ++ " " ++ L.concat (L.intersperse " " (map show atomList))
6672
6773instance Binary Atom where
6874 put atom@(Atom val) = put (atomTypeForAtom atom) >> put val
75+ put (ConstructedAtom dConsName aType atomList) = put aType >> put dConsName >> put atomList
6976 get = do
7077 atomtype <- get
7178 --not-so-great for this to be hardcoded here- it would be nicer to have a dispatch table linked to the instances themselves
7279 --http://stackoverflow.com/questions/8101067/binary-instance-for-an-existential
7380 case atomtype of
7481 AnyAtomType -> error "unsupported serialization AnyAtomType"
82+ caType@(ConstructedAtomType _) -> ConstructedAtom <$> (get :: Get DataConstructorName) <*> pure caType <*> (get :: Get [Atom]) --we really should verify the type with the atoms
7583 (RelationAtomType _) -> Atom <$> (get :: Get Relation)
7684 (AtomType cTypeRep) -> if unCTR cTypeRep == typeRep (Proxy :: Proxy Int) then
7785 Atom <$> (get :: Get Int)
@@ -92,15 +100,19 @@ instance Binary Atom where
92100
93101instance NFData Atom where
94102 rnf !(Atom a) = rnf a
103+ rnf !(ConstructedAtom dConsName aType atomList) = rnf (dConsName, atomList, aType)
95104
96105instance Hashable Atom where
97106 hashWithSalt salt (Atom a) = hashWithSalt salt a
107+ hashWithSalt salt (ConstructedAtom dConsName _ atomList) = salt `hashWithSalt` atomList
108+ `hashWithSalt` dConsName --AtomType is not hashable
98109
99110-- | Return the type of an 'Atom'.
100111atomTypeForAtom :: Atom -> AtomType
101112atomTypeForAtom (Atom atom) = case cast atom of
102113 Just (Relation attrs _) -> RelationAtomType attrs
103114 Nothing -> AtomType $ CTR (typeOf atom)
115+atomTypeForAtom (ConstructedAtom _ aType _) = aType
104116
105117instance Binary UTCTime where
106118 put utc = put $ toRational (utcTimeToPOSIXSeconds utc)
@@ -113,9 +125,12 @@ instance Binary Day where
113125 get = do
114126 (y,m,d) <- get :: Get (Integer, Int, Int)
115127 return (fromGregorian y m d)
116−
128+
129+-- I suspect the definition of ConstructedAtomType with its name alone is insufficient to disambiguate the cases; for example, one could create a type named X, remove a type named X, and re-add it using different constructors. However, as long as requests are served from only one DatabaseContext at-a-time, the type name is unambiguous. This will become a problem for time-travel, however.
130+-- | The AtomType must uniquely identify the type of a atom.
117131data AtomType = AtomType ConcreteTypeRep |
118132 RelationAtomType Attributes |
133+ ConstructedAtomType TypeConstructorName |
119134 AnyAtomType --wildcard used in Atom Functions
120135 deriving (Eq,NFData,Generic,Binary,Show)
121136
@@ -124,6 +139,7 @@ isRelationAtomType :: AtomType -> Bool
124139isRelationAtomType (RelationAtomType _) = True
125140isRelationAtomType _ = False
126141
142+-- | The AttributeName is the name of an attribute in a relation.
127143type AttributeName = StringType
128144
129145-- | A relation's type is composed of attribute names and types.
@@ -256,12 +272,15 @@ data Notification = Notification {
256272 }
257273 deriving (Show, Eq, Binary, Generic)
258274
275+type AtomTypes = M.Map TypeConstructorName (AtomType, AtomConstructor)
276+
259277-- | The DatabaseContext is a snapshot of a database's evolving state and contains everything a database client can change over time.
260278data DatabaseContext = DatabaseContext {
261279 inclusionDependencies :: M.Map IncDepName InclusionDependency,
262280 relationVariables :: M.Map RelVarName Relation,
263281 atomFunctions :: AtomFunctions,
264− notifications :: Notifications
282+ notifications :: Notifications,
283+ atomTypes :: AtomTypes
265284 } deriving (Show, Generic)
266285
267286type IncDepName = StringType
@@ -285,10 +304,24 @@ data DatabaseExpr where
285304
286305 AddNotification :: NotificationName -> RelationalExpr -> RelationalExpr -> DatabaseExpr
287306 RemoveNotification :: NotificationName -> DatabaseExpr
307+
308+ -- this may be generalized to AddAtomFunction in the future
309+ AddAtomConstructor :: TypeConstructorName -> AtomConstructor -> DatabaseExpr
310+ RemoveAtomConstructor :: TypeConstructorName -> DatabaseExpr
311+
312+ -- to implement this, I likely need to implement a DSL for constructing arbitrary functions to operate on atoms at runtime
313+ --AddAtomFunction :: AtomFunction -> DatabaseExpr
314+ --RemoveAtomFunction :: AtomFunctionName -> DatabaseExpr
288315
289316 MultipleExpr :: [DatabaseExpr] -> DatabaseExpr
290317 deriving (Show, Eq, Binary, Generic)
291318
319+type TypeConstructorName = StringType
320+type DataConstructorName = StringType
321+type AtomTypeName = StringType
322+
323+data AtomConstructor = AtomConstructor (M.Map DataConstructorName [AtomTypeName]) deriving (Eq, Show, Binary, Generic)
324+
292325type DatabaseState a = State DatabaseContext a
293326
294327-- | Restriction predicate are boolean algebra components which, when composed, indicate whether or not a tuple should be retained during a restriction (filtering) operation.