on master help sign in
add hair.hs new datatype example along with new datatypes documentation
commit2c0105a04032b3b8912511aa52e3c641add636d3 authorAgentM <agentm@tanyas-imac.local> dateSat 1 Apr 2017, 22:56 (10 years ago) parent7fe25de
5 files changed+104−14
docs/new_datatypes.markdown+48−10 examples/hair.hs+43 examples/out_of_the_tarpit.hs+2−4 project-m36.cabal+8 src/lib/ProjectM36/Client.hs+3
docs/new_datatypes.markdown+48−10
@@ -43,27 +43,65 @@ New algebraic data types can be added to the database context at any time via Tu
4343
4444### Implementation for Haskell Types
4545
46−Given a ```ProjectM36.Client``` ```Connection```, create an ```AddTypeConstructor``` with arguments a) type constructor name and b) data constructors.
46+Any Haskell ADT which implements the ```Atomable``` typeclass can be used directly as a database value or "atom" in Project:M36 parlance. The ```Atomable``` typeclass requires a set of prerequisite typeclasses be included, but, luckily, all of these typeclasses can be derived.
47+
48+Let's implement the ```Hair``` algebraic data type from the TutorialD example, but in Haskell. The full source code is available under the [examples directory](/examples/hair.hs).
49+
50+First, we define our data type and derive all the required typeclasses:
51+
52+```haskell
53+data Hair = Bald | Brown | Blond | OtherColor Text
54+ deriving (Generic, Show, Eq, Binary, NFData, Atomable)
55+```
56+
57+Note that the ```Atomable``` instance can also be derived.
58+
59+After setting up the connection (which we elide here), the database context is loaded with the expression to define the data type.
4760
48−Example:
4961```haskell
50−AddTypeConstructor (ADTypeConstructorDef "Hair" []) [DataConstructorDef "Brown" [], DataConstructorDef "Blond" [], DataConstructorDef "Bald" [],...]
62+executeDatabaseContextExpr sessionId conn (toDatabaseContextExpr (undefined :: Hair))
5163```
5264
53−Pass the ```AddTypeConstructor``` value to ```executeDatabaseContextExpr``` to add it to the database.
65+Using a ```Proxy``` would also be acceptable, but the result would be the same.
5466
55−To create a value for the new algebraic data type, create the type:
67+Next, we can create a relation variable named "people" containing our new data type alongside a person's name.
5668
5769```haskell
58−let hairType = ConstructedAtomType "Hair" M.empty
70+ let blond = NakedAtomExpr (toAtom Blond)
71+ executeDatabaseContextExpr sessionId conn (Assign "people" (MakeRelationFromExprs Nothing [
72+ TupleExpr (M.fromList [("hair", blond), ("name", NakedAtomExpr (TextAtom "Colin"))])]))
5973```
6074
61−then create the value:
75+To demonstrate that the new value is fully integrated with the database, we can create a restriction which matches against the value in the database.
6276
6377```haskell
64−let blondAtom = ConstructedAtom "Blond" hairType []
78+let restrictionPredicate = AttributeEqualityPredicate "hair" blond
79+peopleRelOrErr <- executeRelationalExpr sessionId conn (Restrict restrictionPredicate (RelationVariable "people" ()))
80+```
81+
82+Finally, we can print the resultant relation to show that it can be properly displayed (via the type's ```Show``` instance).
83+
84+```
85+TIO.putStrLn (showRelation peopleRel)
86+```
87+
88+```
89+┌──────────┬──────────┐
90+│hair::Hair│name::Text│
91+├──────────┼──────────┤
92+│Blond │"Colin" │
93+└──────────┴──────────┘
6594```
6695
67−The ```blondAtom``` can now be used in any place where an Atom is otherwise used such as in ```AtomExpr``` checking for equality in restriction or as an argument to ```AtomFunction```s or simply in building a new relation.
96+The essential ```Atomable``` typeclass functions are:
97+
98+| Function | Purpose |
99+| -------- | ------- |
100+| toAtom :: a -> Atom | convert a Haskell data type to a database atom |
101+| fromAtom :: Atom -> a | convert a database atom to a Haskell data type |
102+| toAtomType :: a -> AtomType | generate a database AtomType for a Haskell datatype |
103+| toDatabaseContextExpr :: a -> DatabaseContextExpr | generate a ```DatabaseContextExpr``` which can be executed against a database context in order to add the new type |
104+
105+In addition, basic Haskell data types like ```Int```, ```Text```, ```Bool```, ```UTCTime```, and more already have Atomable instances ready-to-go.
68106
69−In the previous example, the additional empty arguments are for polymorphic variables and arguments. In this simple ADT, there is no polymorphism necessary, so the arguments are empty.
107+To be clear, these data types are not black boxes once stored in the database. These new types can be scanned and manipulated by Haskell scripts which can be created at runtime. For more information, read about [```AtomFunction```s](/docs/atomfunctions.markdown).
examples/hair.hsnew file+43
@@ -0,0 +1,43 @@
1+{-# LANGUAGE DeriveGeneric, DeriveAnyClass, OverloadedStrings #-}
2+import ProjectM36.Client
3+import ProjectM36.Relation.Show.Term
4+import GHC.Generics
5+import Data.Text
6+import Data.Binary
7+import Control.DeepSeq
8+import qualified Data.Map as M
9+import qualified Data.Text.IO as TIO
10+
11+data Hair = Bald | Brown | Blond | OtherColor Text
12+ deriving (Generic, Show, Eq, Binary, NFData, Atomable)
13+
14+main :: IO ()
15+main = do
16+ --connect to the database
17+ let connInfo = InProcessConnectionInfo NoPersistence emptyNotificationCallback []
18+ eCheck v = do
19+ x <- v
20+ case x of
21+ Left err -> error (show err)
22+ Right x' -> pure x'
23+ conn <- eCheck $ connectProjectM36 connInfo
24+
25+ --create a database session at the default branch of the fresh database
26+ sessionId <- eCheck $ createSessionAtHead "master" conn
27+
28+ let mCheck v = v >>= \x -> case x of
29+ Just err -> error (show err)
30+ Nothing -> pure ()
31+ --create the data type in the database context
32+ mCheck $ executeDatabaseContextExpr sessionId conn (toDatabaseContextExpr (undefined :: Hair))
33+
34+ --create a relation with the new Hair AtomType
35+ let blond = NakedAtomExpr (toAtom Blond)
36+ mCheck $ executeDatabaseContextExpr sessionId conn (Assign "people" (MakeRelationFromExprs Nothing [
37+ TupleExpr (M.fromList [("hair", blond), ("name", NakedAtomExpr (TextAtom "Colin"))])]))
38+
39+ let restrictionPredicate = AttributeEqualityPredicate "hair" blond
40+ peopleRel <- eCheck $ executeRelationalExpr sessionId conn (Restrict restrictionPredicate (RelationVariable "people" ()))
41+
42+ TIO.putStrLn (showRelation peopleRel)
43+
examples/out_of_the_tarpit.hs+2−4
@@ -58,7 +58,7 @@ main = do
5858 --create a database session at the default branch of the fresh database
5959 eSessionId <- createSessionAtHead "master" conn
6060 let sessionId = check eSessionId
61−
61+
6262 createSchema sessionId conn
6363
6464createSchema :: SessionId -> Connection -> IO ()
@@ -134,6 +134,4 @@ createSchema sessionId conn = do
134134 --gather up and execute all database updates
135135 mErrs <- mapM (executeDatabaseContextExpr sessionId conn) (new_adts ++ rvDefs ++ incDepKeys ++ incDepForeignKeys)
136136 let errs = catMaybes mErrs
137− when (length errs > 0) (error (show errs))
138−
139−
137+ when (length errs > 0) (error (show errs))
project-m36.cabal+8
@@ -376,6 +376,14 @@ Executable Example-OutOfTheTarpit
376376 Main-Is: examples/out_of_the_tarpit.hs
377377 GHC-Options: -Wall
378378 CPP-Options: -DPROJECTM36_VERSION="v0.1"
379+
380+Executable Example-Hair
381+ Default-Language: Haskell2010
382+ Default-Extensions: OverloadedStrings
383+ Build-Depends: base, HUnit, Cabal, containers, hashable, unordered-containers, mtl, vector, vector-binary-instances, time, hashable-time, bytestring, uuid, stm, deepseq, deepseq-generics, binary, parallel, cassava, attoparsec, gnuplot, directory, temporary, haskeline, megaparsec, text, base64-bytestring, data-interval, filepath, transformers, stm-containers, list-t, aeson, path-pieces, either, conduit, http-api-data, template-haskell, ghc, ghc-paths, project-m36 == 0.1
384+ Main-Is: examples/hair.hs
385+ GHC-Options: -Wall
386+ CPP-Options: -DPROJECTM36_VERSION="v0.1"
379387
380388Test-Suite test-scripts
381389 Default-Language: Haskell2010
src/lib/ProjectM36/Client.hs+3
@@ -78,6 +78,9 @@ module ProjectM36.Client
7878 RemoteProcessDiedException(..),
7979 AtomType(..),
8080 Atomable(..),
81+ TupleExprBase(..),
82+ AtomExprBase(..),
83+ RestrictionPredicateExprBase(..)
8184 ) where
8285import ProjectM36.Base hiding (inclusionDependencies) --defined in this module as well
8386import qualified ProjectM36.Base as B