diff --git a/haskell-experiments/CLAUDE.md b/haskell-experiments/CLAUDE.md index 9ea033e..b84dfbe 100644 --- a/haskell-experiments/CLAUDE.md +++ b/haskell-experiments/CLAUDE.md @@ -26,9 +26,9 @@ Megaparsec-based parser that converts Datalog text into an AST: - `Statement` - Fact, Rule (with `Head` and body literals), or Query - Entry points: `parseDatalog`, `parseDatalogFile` -### Evaluation Engine (`src/Datalog/NaiveDatabase.hs`) -Naive evaluation database storing relations and rules: -- `NaiveDatabase` - Contains `relations` map and `constants` set (Herbrand universe) +### Evaluation Engine (`src/Datalog/InMemoryDB.hs`) +In-memory database storing relations and rules: +- `InMemoryDB` - Contains `relations` map and `constants` set (Herbrand universe) - `Relation` - Name, arity, tuples (facts), and associated rules - `RelationRule` - Head variables and body constraints - Key functions: `withFacts`, `withFactsAndRules` for building databases diff --git a/haskell-experiments/haskell-experiments.cabal b/haskell-experiments/haskell-experiments.cabal index e9ef842..9d70208 100644 --- a/haskell-experiments/haskell-experiments.cabal +++ b/haskell-experiments/haskell-experiments.cabal @@ -82,16 +82,17 @@ test-suite haskell-exps-test Test.SimpleParserSpec, Test.ArithmeticParserSpec, Test.Datalog.DatalogParserSpec, - Test.Datalog.NaiveDatabaseSpec + Test.Datalog.InMemoryDBSpec library langfeatures + default-language: Haskell2010 build-depends: base, containers, megaparsec, parser-combinators, text hs-source-dirs: src exposed-modules: Ologs SimpleParser ArithmeticParser Datalog.DatalogParser - Datalog.NaiveDatabase + Datalog.InMemoryDB Datalog.Rules Datalog.DatalogDB ghc-options: -Wall @@ -99,8 +100,8 @@ library langfeatures OverloadedStrings executable haskell-experiments + default-language: Haskell2010 build-depends: base, containers main-is: Main.hs hs-source-dirs: src - default-language: Haskell2010 diff --git a/haskell-experiments/notes/NaiveDatabase-summary.md b/haskell-experiments/notes/InMemoryDB-summary.md similarity index 92% rename from haskell-experiments/notes/NaiveDatabase-summary.md rename to haskell-experiments/notes/InMemoryDB-summary.md index 739bb19..2e2b614 100644 --- a/haskell-experiments/notes/NaiveDatabase-summary.md +++ b/haskell-experiments/notes/InMemoryDB-summary.md @@ -1,6 +1,6 @@ -# NaiveDatabase.hs Summary +# InMemoryDB.hs Summary -This module implements a naive Datalog database with support for facts, rules, and queries. +This module implements an in-memory Datalog database with support for facts, rules, and queries. ## Data Types @@ -9,7 +9,7 @@ A simple algebraic data type representing values in the database. - `ValueInt Int` - Integer value - `ValueSymbol String` - Symbolic value -### NaiveDatabase +### InMemoryDB The main database structure containing: - `relations :: Map RelationId Relation` - Map of relation names to relations - `constants :: Set Constant` - The Herbrand universe (all constants in the database) @@ -46,9 +46,9 @@ Context maintained while processing rules: - `__relation :: Relation` - The relation being defined - `_headVariables :: [RuleElement]` - Variables collected from head and body - `_bodyConstraints :: [BodyConstraint]` - Constraints from rule body -- `_db :: NaiveDatabase` - Current database state +- `_db :: InMemoryDB` - Current database state -### NaiveDatabaseException +### InMemoryDBException Exception types for error handling: - `CannotParseStatementException` - Failed to parse Datalog text - `NonFactException` - Expected a fact but got another statement type diff --git a/haskell-experiments/notes/NaiveDatabase_classes.dot b/haskell-experiments/notes/InMemoryDB_classes.dot similarity index 76% rename from haskell-experiments/notes/NaiveDatabase_classes.dot rename to haskell-experiments/notes/InMemoryDB_classes.dot index 5cfd5e2..10190ed 100644 --- a/haskell-experiments/notes/NaiveDatabase_classes.dot +++ b/haskell-experiments/notes/InMemoryDB_classes.dot @@ -1,10 +1,10 @@ -digraph NaiveDatabase { +digraph InMemoryDB { rankdir=TB; node [shape=record, fontname="Helvetica", fontsize=10]; edge [fontname="Helvetica", fontsize=9]; // Main database - NaiveDatabase [label="{NaiveDatabase|relations : Map RelationId Relation\lconstants : Set Constant\l}"]; + InMemoryDB [label="{InMemoryDB|relations : Map RelationId Relation\lconstants : Set Constant\l}"]; // Core data types Relation [label="{Relation|_name : Text\l_arity : Int\l_tuples : Set [Constant]\l_rules : [RelationRule]\l}"]; @@ -18,7 +18,7 @@ digraph NaiveDatabase { BodyConstraint [label="{BodyConstraint|_relation : Relation\l_elements : [ConstraintElement]\l}"]; - RuleContext [label="{RuleContext|__relation : Relation\l_headVariables : [RuleElement]\l_bodyConstraints : [BodyConstraint]\l_db : NaiveDatabase\l}"]; + RuleContext [label="{RuleContext|__relation : Relation\l_headVariables : [RuleElement]\l_bodyConstraints : [BodyConstraint]\l_db : InMemoryDB\l}"]; // Type aliases (shown as notes) TypeAliases [shape=note, label="Type Aliases\l\lConstant = Term\lRelationId = Text\l"]; @@ -27,10 +27,10 @@ digraph NaiveDatabase { Value [label="{Value|ValueInt Int\l| ValueSymbol String\l}"]; // Exception type - NaiveDatabaseException [label="{NaiveDatabaseException|CannotParseStatementException\l| NonFactException\l| NonRuleException\l| NonQueryException\l| BadArityException\l| VariableLookupException\l| UnexpectedConstantException\l}"]; + InMemoryDBException [label="{InMemoryDBException|CannotParseStatementException\l| NonFactException\l| NonRuleException\l| NonQueryException\l| BadArityException\l| VariableLookupException\l| UnexpectedConstantException\l}"]; // Relationships - NaiveDatabase -> Relation [label="contains *", style=solid]; + InMemoryDB -> Relation [label="contains *", style=solid]; Relation -> RelationRule [label="has *", style=solid]; RelationRule -> Relation [label="references *", style=dashed]; RelationRule -> RuleElement [label="contains *", style=solid]; @@ -41,14 +41,14 @@ digraph NaiveDatabase { RuleContext -> Relation [label="references", style=dashed]; RuleContext -> RuleElement [label="contains *", style=solid]; RuleContext -> BodyConstraint [label="contains *", style=solid]; - RuleContext -> NaiveDatabase [label="references", style=dashed]; + RuleContext -> InMemoryDB [label="references", style=dashed]; // Grouping subgraph cluster_core { label="Core Types"; style=rounded; color=blue; - NaiveDatabase; Relation; RelationRule; RuleElement; + InMemoryDB; Relation; RelationRule; RuleElement; } subgraph cluster_processing { @@ -62,6 +62,6 @@ digraph NaiveDatabase { label="Other"; style=rounded; color=gray; - Value; NaiveDatabaseException; TypeAliases; + Value; InMemoryDBException; TypeAliases; } } diff --git a/haskell-experiments/src/Datalog/NaiveDatabase.hs b/haskell-experiments/src/Datalog/InMemoryDB.hs similarity index 86% rename from haskell-experiments/src/Datalog/NaiveDatabase.hs rename to haskell-experiments/src/Datalog/InMemoryDB.hs index 4d250df..0d85d89 100644 --- a/haskell-experiments/src/Datalog/NaiveDatabase.hs +++ b/haskell-experiments/src/Datalog/InMemoryDB.hs @@ -6,7 +6,7 @@ {-# LANGUAGE InstanceSigs #-} {-# LANGUAGE BlockArguments #-} -module Datalog.NaiveDatabase where +module Datalog.InMemoryDB where import Control.Exception.Base import Data.Map (Map) @@ -18,30 +18,30 @@ import Datalog.DatalogParser (Literal (..), Statement (..), parseDatalog) import Datalog.Rules import Datalog.DatalogDB -data NaiveDatabase = NaiveDatabase +data InMemoryDB = InMemoryDB { relations :: Map RelationId Relation , constants :: Set Constant } deriving (Show, Eq) -instance DatalogDB NaiveDatabase where - emptyDB :: NaiveDatabase - emptyDB = NaiveDatabase +instance DatalogDB InMemoryDB where + emptyDB :: InMemoryDB + emptyDB = InMemoryDB { relations = Map.empty , constants = Set.empty -- the Herbrand universe } - lookupRelation :: NaiveDatabase -> Text -> Maybe Relation + lookupRelation :: InMemoryDB -> Text -> Maybe Relation lookupRelation db relationName = Map.lookup relationName $ relations db - insertRelation :: NaiveDatabase -> Relation -> NaiveDatabase + insertRelation :: InMemoryDB -> Relation -> InMemoryDB insertRelation db relation = db { relations = Map.insert (_name relation) relation (relations db) } - addConstants :: NaiveDatabase -> Set Constant -> NaiveDatabase + addConstants :: InMemoryDB -> Set Constant -> InMemoryDB addConstants db newConstants = db { constants = Set.union newConstants (constants db) @@ -78,7 +78,7 @@ withFacts = Right otherStatement -> throw $ NonFactException factText otherStatement Left ex -> throw $ CannotParseStatementException factText ex -withFactsAndRules :: [Text] -> [Text] -> NaiveDatabase +withFactsAndRules :: [Text] -> [Text] -> InMemoryDB withFactsAndRules facts = foldr (addRule . extractRule) (withFacts facts) query :: forall db . (DatalogDB db) => db -> Text -> Text @@ -87,4 +87,3 @@ query db qText = Right (Query texts literals) -> "#NYI" Right otherStatement -> throw $ NonQueryException qText otherStatement Left ex -> throw $ CannotParseStatementException qText ex - diff --git a/haskell-experiments/test/Main.hs b/haskell-experiments/test/Main.hs index 6893b71..5b53273 100644 --- a/haskell-experiments/test/Main.hs +++ b/haskell-experiments/test/Main.hs @@ -5,7 +5,7 @@ import qualified Test.OlogsSpec as Ologs import qualified Test.SimpleParserSpec as SimpleParserSpec import qualified Test.ArithmeticParserSpec as ArithmeticParserSpec import qualified Test.Datalog.DatalogParserSpec as DatalogParserSpec -import qualified Test.Datalog.NaiveDatabaseSpec as NaiveDatabaseSpec +import qualified Test.Datalog.InMemoryDBSpec as InMemoryDBSpec main :: IO () main = hspec $ do @@ -13,5 +13,5 @@ main = hspec $ do describe "SimpleParser" SimpleParserSpec.spec describe "ArithmeticParser" ArithmeticParserSpec.spec describe "DatalogParser" DatalogParserSpec.spec - describe "NaiveDatabase" NaiveDatabaseSpec.spec + describe "InMemoryDB" InMemoryDBSpec.spec diff --git a/haskell-experiments/test/Test/Datalog/NaiveDatabaseSpec.hs b/haskell-experiments/test/Test/Datalog/InMemoryDBSpec.hs similarity index 94% rename from haskell-experiments/test/Test/Datalog/NaiveDatabaseSpec.hs rename to haskell-experiments/test/Test/Datalog/InMemoryDBSpec.hs index 3662a64..c928b08 100644 --- a/haskell-experiments/test/Test/Datalog/NaiveDatabaseSpec.hs +++ b/haskell-experiments/test/Test/Datalog/InMemoryDBSpec.hs @@ -12,22 +12,22 @@ {-# LANGUAGE NoMonomorphismRestriction #-} {-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} -module Test.Datalog.NaiveDatabaseSpec where +module Test.Datalog.InMemoryDBSpec where import Data.Map qualified as Map import Data.Set qualified as Set import Datalog.DatalogParser -import Datalog.NaiveDatabase -import Datalog.NaiveDatabase qualified as NaiveDatabase +import Datalog.InMemoryDB +import Datalog.InMemoryDB qualified as InMemoryDB import Test.Hspec import Datalog.DatalogDB spec :: Spec spec = do - describe "NaiveDatabase operations" do + describe "InMemoryDB operations" do it "can ingest facts into relations & a universe" $ do let db = - NaiveDatabase.withFacts + InMemoryDB.withFacts [ "parent(\"alice\", \"bob\")." , "parent(\"bob\", \"carol\")." ] @@ -42,7 +42,7 @@ spec = do ] it "can ingest facts and rules" do let db = - NaiveDatabase.withFactsAndRules + InMemoryDB.withFactsAndRules [ "parent(\"alice\", \"bob\")." , "parent(\"bob\", \"carol\")." ] @@ -105,7 +105,7 @@ spec = do it "can ingest facts and rules with constants" do let db = - NaiveDatabase.withFactsAndRules + InMemoryDB.withFactsAndRules [] ["ancestor(X,\"patriarch\") :- ."] ancestorRule = @@ -128,7 +128,7 @@ spec = do it "can ingest facts and rules with duplicate head entries" do let db = - NaiveDatabase.withFactsAndRules + InMemoryDB.withFactsAndRules [] ["equivalent(Q,Q) :- ."] equivalentRule = @@ -151,7 +151,7 @@ spec = do it "can ingest facts and rules with duplicate head entries" do let db = - NaiveDatabase.withFactsAndRules + InMemoryDB.withFactsAndRules [] ["equivalent(Q,Q) :- ."] rule1 = @@ -175,7 +175,7 @@ spec = do it "can ingest a theory of equivalence relations" do let db = - NaiveDatabase.withFactsAndRules + InMemoryDB.withFactsAndRules [] [ "equivalent(Q,Q) :- ." , "equivalent(R,Q) :- equivalent(Q,R)." @@ -233,8 +233,8 @@ spec = do `shouldBe` Set.empty it "can do basic queries" do - let db :: NaiveDatabase = - NaiveDatabase.withFacts + let db :: InMemoryDB = + InMemoryDB.withFacts [ "parent(\"alice\", \"bob\")." , "parent(\"bob\", \"carol\")." ]