Closed alex-dixon closed 7 years ago
This feature appears to require a rule to return one or more generated rules in addition to itself.
(defsub :task-list
[(<- ?ordered-visible-todos (entities (by-fact-id :e) :from [:todo/visible]))]
[[_ :active-count ?active-count]]
=>
{:visible-todos ?ordered-visible-todos
:active-count ?active-count})
Note we can hone the syntax for kicking off list generation as we go along. Provided is just an best example I've come up with so far.
entities
could signal the generation of the following rules:
(def-tuple-rule create-list-of-visible-todos
{:group :report}
[?eids <- (by-fact-id :e) :from [:todo/visible]]
[:test (seq ?eids)]
=>
(insert! [(guid) :todos/by-last-modified*order ?eids])
(doseq [x ?eids]
(insert! [(guid) :todos/by-last-modified*eid x])))
(def-tuple-rule update-list-of-visible-todos
{:group :report}
[[_ :todos/by-last-modified*eid ?e]]
[(<- ?entity (entity ?e))]
=>
(trace "Entity list!" ?entity)
(insert! [(guid) :todos/by-last-modified*item ?entity]))
(defsub :task-list
[[_ :todos/by-last-modified*order ?eids]]
[?items <- (acc/all :v) :from [:todos/by-last-modified*item]]
[[_ :active-count ?active-count]]
[:test (seq ?eids)]
=>
(let [items (group-by :e (flatten ?items))
ordered (vals (select-keys items (into [] ?eids)))
entities (util/entity-Tuples->entity-maps ordered)]
(trace "Entities" entities)
{:visible-todos entities
:all-complete? (= 0 ?active-count)}))
This should still allow users to insert the list as a fact like so:
(def-tuple-rule list-visible-todos-by-last-modified
[(<- ?ordered-visible-todos (entities (by-fact-id :e) :from [:todo/visible]))]
=>
(insert! [(guid) :todos/by-last-modified ?ordered-visible-todos]))
The biggest challenge seems to be dynamic rule generation in Clojurescript. We've already had problems with more complex DSL implementations (#40). It also appears there's no intern
.
It might be beneficial to borrow liberally from mfikes' Planck library, in particular these functions:
First pass at how we might implement entities
:
;; Usage:
;; User writes:
(rule my-rule
[?eids <- (acc/all :e) :from [:interesting-fact]] ;; Maybe new special form `(eids [:interesting-fact])`
[(<- ?interesting-entities (entities ?eids))]
=>
;; Prints list of Tuples
(println "Found entities with interesting fact" ?interesting-entities))
;; We generate:
;; A rule that contains the accumulator expression with the binding provided in `(entities)`
(rule my-rule___split-0
[?eids <- (acc/all :e) :from [:interesting-fact]
=>
(let [req-id (guid)]
gen-fact-req [req-id ::gen-fact/request :entities]
_ (swap! state/generated-facts assoc req-id {:req gen-fact-req :rule-name ???})
(insert! gen-fact-req)
(insert! [req-id :entities/order ?eids])
(doseq [eid ?eids] (insert! [req-id :entities/eid ?eid])))])
;: And the original, rewritten to match on the ::gen-fact/response, keeping the same name
(rule my-rule
;; ...rest LHS
;; Want to remove acc expr (it exists in a genned rule),
;; but then no access to its bound variable inside this rule...
[?eids <- (acc/all :e) :from [:interesting-fact]]
;; Replaces `entities` on same "line" as original
[[req-id ::gen-fact/response ?interesting-entities]]
=>
;; ...original RHS
;; precept.rules.impl:
;; These are "always on", waiting for an :entity request
(rule entities___impl-a
[[?req ::gen-fact/request :entities]]
[[?req :entities/eid ?e]]
[[(<- ?entity (entity ?e))]]
=>
(insert! [?req :entities/entity ?e]))
(rule entities___impl-b
[[?req :entities/order ?eids]]
[?ents <- (acc/all :v) :from [?req :entities/entity]]
=>
(let [items (group-by :e (flatten ?ents))
ordered (vals (select-keys items (into [] ?eids)))
(insert! [?req ::gen-fact/response ordered]))
Implemented very closely to the outline above in #57. 🎉
We should be able to have an api for the following:
The end result is a list of entities in a specified order. We could have rules that do this under the covers and surface the resulting list as a fact.