A question from the book, in the VIP-status example section on rules:
(def reasoning-rules
'[;; Rule 1: High Spender
[(is-high-spender? ?u)
[?u :user/total-spent ?amount]
[(> ?amount 5000)]]
;; Rule 2: Frequent Shopper
[(is-frequent-shopper? ?u)
[?u :user/order-count ?count]
[(> ?count 20)]]
;; Rule 3: Reasoner for VIP Status
[(vip-status ?u ?tier)
(is-high-spender? ?u)
(is-frequent-shopper? ?u)
[(ground :gold) ?tier]]
[(vip-status ?u ?tier)
(or (is-high-spender? ?u)
(is-frequent-shopper? ?u))
[(ground :silver) ?tier]]])
The two clauses for vip-status implies that the order of rules is important, correct? Otherwise, if I understand correctly, the :silver status would be liable to be assigned to vips that would also qualify for :gold.
In a thread from 2024 I see this https://clojurians.slack.com/archives/C01RD3AF336/p1725641324831899?thread_ts=1725634881.640399&cid=C01RD3AF336:
> The current rule engine processes rules in the order as you write them, so rearranging them makes a big difference.
> The rewritten engine will be order independent.
Unsure if this is still the case, I see the rules list referred to as a set/rule set--which to me implies un-ordered by default, but of course there is sorted-set in clojure.core, so I shouldn't read that word-choice as a be-all end-allRules order does not matter. So yes, a user qualifying for silver would also qualify for gold. So that example needs to add rule to exclude those satisfy both conditions.
I will correct the web version of the book, but the paper book has already been submitted, so it will need to be in the errata.
Thanks for pointing this out.
I appreciate the clarification, wish I had gotten to this section sooner during the review window- jumped around too much 😓
Thank you for catching this problem. 🙏
Another basic rule-based misunderstanding I might be having, which is causing me some confusion: Given the following database:
(def tconn
(d/get-conn "/tmp/datalevin/tst"
{:a/name {:db/valueType :db.type/string}
:a/b {:db/valueType :db.type/ref}
:b/name {:db/valueType :db.type/string}
:b/c {:db/valueType :db.type/ref}
:c/name {:db/valueType :db.type/string}
:c/d {:db/valueType :db.type/ref}
:d/name {:db/valueType :db.type/string}
:d/e {:db/valueType :db.type/ref}
:e/name {:db/valueType :db.type/string}}))
(d/transact! tconn
[{:db/id 1
:a/name "A"
:a/b {:db/id 2
:b/name "B"
:b/c {:db/id 3
:c/name "C"
:c/d {:db/id 4
:d/name "D"
:d/e {:db/id 5
:e/name "E"}}}}}])
The following query/ruleset evaluates as I expect:
(d/q
'[:find (pull ?a [:*])
:in $ % ?subject
:where
[?e :e/name ?subject]
(e->a ?e ?a)
[?a :a/b ?b]]
(d/db tconn)
'[[(b->d ?b ?d), [?b :b/c ?c], [?c :c/d ?d]]
[(a->d ?a ?d), [?a :a/b ?b], (b->d ?b ?d)]
[(e->a ?e ?a), [?d :d/e ?e], (a->d ?a ?d)]]
"E")
;;=> ([{:db/id 1, :a/name "A", :a/b #:db{:id 2}}])
But if I alter the :find to pull ?b instead, I get an empty list (), even though (to me, at least) the previous result implies ?b should be populated
If I elide some rule usage however, ?b ends up properly bound
(d/q
'[:find (pull ?b [:*])
:in $ % ?subject
:where
[?e :e/name ?subject]
;; (e->a ?e ?a) | supplanted by the following 2 clauses
[?d :d/e ?e]
(b->d ?b ?d)
[?a :a/b ?b]]
(d/db tconn)
'[[(b->d ?b ?d), [?b :b/c ?c], [?c :c/d ?d]]
[(a->d ?a ?d), [?a :a/b ?b], (b->d ?b ?d)]
[(e->a ?e ?a), [?d :d/e ?e], (a->d ?a ?d)]]
"E")
;;=> ([{:db/id 2, :b/name "B", :b/c #:db{:id 3}}])
Why does pulling ?b in the first query fail, but not in the second one?This is a bug that has already been fixed in current master.
It as #376
Ah I see; thanks, will try and run on that for the time being