Skip to content

Commit

Permalink
go back to the single-construction API
Browse files Browse the repository at this point in the history
  • Loading branch information
BrianHicks committed Jun 14, 2021
1 parent e32342b commit 2d2b925
Show file tree
Hide file tree
Showing 3 changed files with 289 additions and 194 deletions.
84 changes: 50 additions & 34 deletions sample-apps/src/FamilyTree.elm
Expand Up @@ -327,43 +327,55 @@ viewRelationships model personId =
derived =
Datalog.derive
[ {- me(id, name) :- person(id, name), id = personId -}
Datalog.rule "me" [ "id", "name" ]
|> Datalog.with "person" [ Datalog.var "id", Datalog.var "name" ]
|> Datalog.filter (Datalog.eq "id" (Datalog.int personId))
Datalog.rule "me"
[ "id", "name" ]
[ Datalog.with "person" [ Datalog.var "id", Datalog.var "name" ]
, Datalog.filter (Datalog.eq "id" (Datalog.int personId))
]
, {- parents(id, name) :- person(id, name), parent(id, personId). -}
Datalog.rule "parents" [ "id", "name" ]
|> Datalog.with "person" [ Datalog.var "id", Datalog.var "name" ]
|> Datalog.with "parent" [ Datalog.var "id", Datalog.int personId ]
Datalog.rule "parents"
[ "id", "name" ]
[ Datalog.with "person" [ Datalog.var "id", Datalog.var "name" ]
, Datalog.with "parent" [ Datalog.var "id", Datalog.int personId ]
]
, {- children(id, name) :- person(id, name), parent(personId, id). -}
Datalog.rule "children" [ "id", "name" ]
|> Datalog.with "person" [ Datalog.var "id", Datalog.var "name" ]
|> Datalog.with "parent" [ Datalog.int personId, Datalog.var "id" ]
Datalog.rule "children"
[ "id", "name" ]
[ Datalog.with "person" [ Datalog.var "id", Datalog.var "name" ]
, Datalog.with "parent" [ Datalog.int personId, Datalog.var "id" ]
]
, {-
siblings(siblingId, siblingName) :-
person(siblingId, siblingName),
parent(parentId, personId),
parent(parentId, siblingId),
siblingId /= personId.
-}
Datalog.rule "siblings" [ "siblingId", "siblingName" ]
|> Datalog.with "person" [ Datalog.var "siblingId", Datalog.var "siblingName" ]
|> Datalog.with "parent" [ Datalog.var "parentId", Datalog.int personId ]
|> Datalog.with "parent" [ Datalog.var "parentId", Datalog.var "siblingId" ]
|> Datalog.filter (Datalog.not_ (Datalog.eq "siblingId" (Datalog.int personId)))
Datalog.rule "siblings"
[ "siblingId", "siblingName" ]
[ Datalog.with "person" [ Datalog.var "siblingId", Datalog.var "siblingName" ]
, Datalog.with "parent" [ Datalog.var "parentId", Datalog.int personId ]
, Datalog.with "parent" [ Datalog.var "parentId", Datalog.var "siblingId" ]
, Datalog.filter (Datalog.not_ (Datalog.eq "siblingId" (Datalog.int personId)))
]
, {-
grandparents(grandparentId, grandparentName) :-
person(grandparentId, grandparentName),
person(parentId, personId),
person(grandparentId, parentId).
-}
Datalog.rule "grandparents" [ "grandparentId", "grandparentName" ]
|> Datalog.with "person" [ Datalog.var "grandparentId", Datalog.var "grandparentName" ]
|> Datalog.with "parent" [ Datalog.var "parentId", Datalog.int personId ]
|> Datalog.with "parent" [ Datalog.var "grandparentId", Datalog.var "parentId" ]
, Datalog.rule "grandchildren" [ "grandchildId", "grandchildName" ]
|> Datalog.with "person" [ Datalog.var "grandchildId", Datalog.var "grandchildName" ]
|> Datalog.with "parent" [ Datalog.var "childId", Datalog.var "grandchildId" ]
|> Datalog.with "parent" [ Datalog.int personId, Datalog.var "childId" ]
Datalog.rule "grandparents"
[ "grandparentId", "grandparentName" ]
[ Datalog.with "person" [ Datalog.var "grandparentId", Datalog.var "grandparentName" ]
, Datalog.with "parent" [ Datalog.var "parentId", Datalog.int personId ]
, Datalog.with "parent" [ Datalog.var "grandparentId", Datalog.var "parentId" ]
]
, Datalog.rule "grandchildren"
[ "grandchildId", "grandchildName" ]
[ Datalog.with "person" [ Datalog.var "grandchildId", Datalog.var "grandchildName" ]
, Datalog.with "parent" [ Datalog.var "childId", Datalog.var "grandchildId" ]
, Datalog.with "parent" [ Datalog.int personId, Datalog.var "childId" ]
]
, {-
auntsAndUncles(auId, auName) :-
person(auId, auName),
Expand All @@ -372,18 +384,22 @@ viewRelationships model personId =
parent(parentId, personId),
auId /= parentId.
-}
Datalog.rule "auntsAndUncles" [ "auId", "auName" ]
|> Datalog.with "person" [ Datalog.var "auId", Datalog.var "auName" ]
|> Datalog.with "parent" [ Datalog.var "grandparentId", Datalog.var "auId" ]
|> Datalog.with "parent" [ Datalog.var "grandparentId", Datalog.var "parentId" ]
|> Datalog.with "parent" [ Datalog.var "parentId", Datalog.int personId ]
|> Datalog.filter (Datalog.not_ (Datalog.eq "auId" (Datalog.var "parentId")))
, Datalog.rule "niecesAndNephews" [ "nnId", "nnName" ]
|> Datalog.with "person" [ Datalog.var "nnId", Datalog.var "nnName" ]
|> Datalog.with "parent" [ Datalog.var "siblingId", Datalog.var "nnId" ]
|> Datalog.with "parent" [ Datalog.var "parentId", Datalog.var "siblingId" ]
|> Datalog.with "parent" [ Datalog.var "parentId", Datalog.int personId ]
|> Datalog.filter (Datalog.not_ (Datalog.eq "siblingId" (Datalog.int personId)))
Datalog.rule "auntsAndUncles"
[ "auId", "auName" ]
[ Datalog.with "person" [ Datalog.var "auId", Datalog.var "auName" ]
, Datalog.with "parent" [ Datalog.var "grandparentId", Datalog.var "auId" ]
, Datalog.with "parent" [ Datalog.var "grandparentId", Datalog.var "parentId" ]
, Datalog.with "parent" [ Datalog.var "parentId", Datalog.int personId ]
, Datalog.filter (Datalog.not_ (Datalog.eq "auId" (Datalog.var "parentId")))
]
, Datalog.rule "niecesAndNephews"
[ "nnId", "nnName" ]
[ Datalog.with "person" [ Datalog.var "nnId", Datalog.var "nnName" ]
, Datalog.with "parent" [ Datalog.var "siblingId", Datalog.var "nnId" ]
, Datalog.with "parent" [ Datalog.var "parentId", Datalog.var "siblingId" ]
, Datalog.with "parent" [ Datalog.var "parentId", Datalog.int personId ]
, Datalog.filter (Datalog.not_ (Datalog.eq "siblingId" (Datalog.int personId)))
]
]
model.db

Expand Down
92 changes: 53 additions & 39 deletions src/Datalog.elm
Expand Up @@ -392,35 +392,39 @@ If you have multiple rules with the same name, they'll be merged together
(for an example, see the docs for [`with`](#with).)
-}
rule : String -> List String -> Rule
rule name headVars =
Rule (Atom name (List.map Variable headVars)) []
rule : String -> List String -> List BodyAtom -> Rule
rule name headVars atoms =
Rule (Atom name (List.map Variable headVars)) atoms


{-| Add matches from the given name (TODO: table? rule? named tuple store?)
For example, if you have some greeks (Socrates, say) you can write a rule
like this to see which of them are mortal:
rule "mortal" [ "name" ]
|> with "greek" [ var "name" ]
rule "mortal"
[ "name" ]
[ with "greek" [ var "name" ] ]
It's fine to use this to set up recursive queries. For example, you could
compute reachability for all nodes in a graph using two rules like this:
[ rule "reachable" [ "a", "b" ]
|> with "link" [ var "a", var "b" ]
, rule "reachable" [ "a", "c" ]
|> with "link" [ var "a", var "b" ]
|> with "reachable" [ var "b", var "c" ]
[ rule "reachable"
[ "a", "b" ]
[ with "link" [ var "a", var "b" ] ]
, rule "reachable"
[ "a", "c" ]
[ with "link" [ var "a", var "b" ]
, with "reachable" [ var "b", var "c" ]
]
]
If you introduce a variable in a `with` like that above, it's also fine!
-}
with : String -> List Term -> Rule -> Rule
with name terms (Rule head body) =
Rule head (BodyAtom Positive (Atom name terms) :: body)
with : String -> List Term -> BodyAtom
with name terms =
BodyAtom Positive (Atom name terms)


{-| The opposite of [`with`](#with): remove any matching tuples based on
Expand All @@ -441,30 +445,40 @@ Here's an example of computing all the nodes in a graph that _aren't_
reachable from each other:
[ -- first, define `reachable` as in the example in `with`:
rule "reachable" [ "a", "b" ]
|> with "link" [ var "a", var "b" ]
, rule "reachable" [ "a", "c" ]
|> with "link" [ var "a", var "b" ]
|> with "reachable" [ var "b", var "c" ]
rule "reachable"
[ "a", "b" ]
[ with "link" [ var "a", var "b" ] ]
, rule "reachable"
[ "a", "c" ]
[ with "link"
[ var "a", var "b" ]
with
"reachable"
[ var "b", var "c" ]
]
-- next, we need to know what is a valid node so we can
, rule "node" [ "a" ]
|> with "link" [ var "a", var "b" ]
, rule "node" [ "b" ]
|> with "link" [ var "a", var "b" ]
, rule "node"
[ "a" ]
[ with "link" [ var "a", var "b" ] ]
, rule "node"
[ "b" ]
[ with "link" [ var "a", var "b" ] ]
-- finally, we just say "a set of two nodes is unreachable if they're
-- individually in `node` but not together in `reachable`"
, rule "unreachable" [ "a", "b" ]
|> with "node" [ var "a" ]
|> with "node" [ var "b" ]
|> without "reachable" [ var "a", var "b" ]
, rule "unreachable"
[ "a", "b" ]
[ with "node" [ var "a" ]
, with "node" [ var "b" ]
, without "reachable" [ var "a", var "b" ]
]
]
-}
without : String -> List Term -> Rule -> Rule
without name terms (Rule head body) =
Rule head (BodyAtom Negative (Atom name terms) :: body)
without : String -> List Term -> BodyAtom
without name terms =
BodyAtom Negative (Atom name terms)


planRule : Rule -> Result Problem Database.QueryPlan
Expand Down Expand Up @@ -689,9 +703,9 @@ type Op
| Lt


filter : Filter -> Rule -> Rule
filter filter_ (Rule head body) =
Rule head (Filter filter_ :: body)
filter : Filter -> BodyAtom
filter =
Filter


eq : String -> Term -> Filter
Expand Down Expand Up @@ -899,15 +913,15 @@ parserHelp soFar =

ruleParser : Parser Rule
ruleParser =
Parser.succeed (List.foldl (\with_ rule_ -> with_ rule_))
Parser.succeed (\( name, headTerms ) bodyTerms -> rule name headTerms bodyTerms)
|= ruleHeadParser
|. spacesAndComments
|= ruleBodyParser


ruleHeadParser : Parser Rule
ruleHeadParser : Parser ( String, List String )
ruleHeadParser =
Parser.succeed rule
Parser.succeed Tuple.pair
|= Parser.inContext NameOfRule nameParser
|. spacesAndComments
|= Parser.sequence
Expand All @@ -921,7 +935,7 @@ ruleHeadParser =
|> Parser.inContext RuleHead


ruleBodyParser : Parser (List (Rule -> Rule))
ruleBodyParser : Parser (List BodyAtom)
ruleBodyParser =
Parser.sequence
{ start = hornToken
Expand All @@ -937,7 +951,7 @@ ruleBodyParser =
}


bodyAtomParser : Parser (Rule -> Rule)
bodyAtomParser : Parser BodyAtom
bodyAtomParser =
Parser.succeed
(\negative name body ->
Expand Down Expand Up @@ -970,14 +984,14 @@ notParser =
]


filterParser : Parser (Rule -> Rule)
filterParser : Parser BodyAtom
filterParser =
Parser.andThen
(\firstFilter -> Parser.loop firstFilter filterParserHelp)
filterClauseParser


filterParserHelp : Filter -> Parser (Parser.Step Filter (Rule -> Rule))
filterParserHelp : Filter -> Parser (Parser.Step Filter BodyAtom)
filterParserHelp lastFilter =
Parser.oneOf
[ Parser.succeed (\nextFilter -> Parser.Loop (or lastFilter nextFilter))
Expand Down

0 comments on commit 2d2b925

Please sign in to comment.