Closed nivekuil closed 9 months ago
If you are doing a swap!
on the session atom from inside a rule, that sounds like a problem. Mutating an atom from inside another swap!
fn in general is a bad idea, whether you're using odoyle or not. I'm not sure why this would happen with browser events like pointerup
; you can mutate an atom from various browser events and trigger your rules just fine. It sounds like one or more of your rules are ultimately trying to mutate an atom, though. Is there a reason they need to do this?
Is there a reason they need to do this?
Bridging odoyle to electric means that we lose dynamic binding conveyance from clojure to electric, it's got its own semantics.
My solution has been to make this change to o/fire-rules
+ (let [session (cond-> session (:transform opts) ((:transform opts)))
then-queue (:then-queue session)
then-finally-queue (:then-finally-queue session)]
Then in my code I can queue up work instead of touching *mutable-session*
:
(def fact-queue (js/Array.))
(defn add-work! [f]
(.push fact-queue f))
(defn fire-rules [s]
(o/fire-rules s {:transform (fn [session]
(let [s (reduce (fn[s f](f s)) session fact-queue)]
(set! fact-queue (js/Array.))
s))}))
(add-work! (fn [s](o/insert s e a v)))
I think something like this is also necessary for a multi threaded fire-rules, though I'm only concerned with cljs right now.
I'm not sure why this would happen with browser events like pointerup
this was probably a red herring, haven't seen an issue with this since I made this change
Hi, so with the new dynamic rule stuff I've been building a framework that combines odoyle and asami for a reactive, forward and backward-chaining datalog store designed to serve both web apps and games. In combination with Electric clojure this augments odoyle with ergonomic multi-cardinality joins, cleanup handlers (== truth maintenance), maybe even distributed storage later -- so far it's been looking great.
One of the challenges I've run into is mixing odoyle managed mutations like
o/insert!
, which operate on a*mutable-session*
, and mutations from outside a rule, which wouldswap!
the session atom. I think the way odoyle does it creates a race condition:fire-rules
basically takes ownership of the session with interior mutability, and any changes to the session that happen whilefire-rules
is going off they will be lost when the session atom is reset to the result offire-rules
.This can happen with relatively plain odoyle use. Take for example my recent game jam submission: when you start dragging the launcher it registers an
pointerup
event handler which inserts the rules to launch the ball. It's hard to trigger in the published demo but as I've been pushing it it happens relatively often that thepointerup
event gets essentially skipped because it's processed in the middle offire-rules
.Thinking about it a bit more I think I should just not use
fire-rules
at all, useswap!
instead ofo/reset!
everywhere and write my own impurefire-rules!
.