The working memory store is typically used by the event queue, the external one, to load and persist the session memory in between external events. The internal event queue doesn't cause storage events. So, that would give you what I was describing above, but it still would not give you visibility into the internal movements caused by raise nodes etc.
See the w3c spec. The algorithm has to continue running the chart for any given external event until the transitions within the chart stop causing movement. The internal event queue is a very simple queue. It isn't meant to interact with the rest of the world, doesn't have any timing, capabilities, etc. It's just a place to put events that have been set as necessary to stabilize the state chart. Unless you create some sort of crazy infinite loop in your chart, this typically happens in microseconds.
The only reason the evaluation of an external event will pause or block in any way is because one of your expressions steals the thread and blocks. So, you aren't in any sort of stable configuration at that point. The chart is still figuring out how to get there.
Sorry, I think the way I formatted my first post buried my last question - I edited that post to make it clearer. In the defrecord example above I'm specifically asking about question 3, "I want to be able to react to any change to the configuration or data model of a session". I don't need to know about internal state transitions, just settled states of the chart. With those clarifications, does this defrecord seem like a reasonable approach? It seems to work I just don't know if there's a better way.
Yes, putting something in either the implementation of the external event queue (which pulls the working mem, runs a step, then saves it) or the working memory store (which is triggered by this process) will get you what you want. I’d say from a “clean” perspective, the working memory store protocol could be accessed for non-related reasons. “external events” are really the only thing that would ever cause a config change (“timed” things always go through the external event queue). So, semantically, I think the event queue is the “right” thing to instrument, but what you’ve done “works”, it might just lead to weirdness if you even access that protocol for your own reasons that come up down the line.
Great, thanks a lot for the help!
@tony.kay I don't want to pester you w/ a bunch of PRs at once but in addition to the one I submitted earlier there appears to be a bug in com.fulcrologic.statecharts.invocation.future. The sp/send! when the src completes doesn't include :invoke-id, so finalize elements aren't notified; a script in a finalize won't execute.
And one more: even with the fix for the above applied, data model assignments done in finalize script elements are not applied until after any transitions, which conflicts w/ the spec. Example:
(sce/state {:id :loading-warnings}
(sce/invoke {:id :load-warnings
:type :future
:src (fn [_] :bar)}
(sce/finalize {}
(sce/script {:expr (fn [env data]
(let [result (get-in data [:_event :data])]
[(ops/assign :foo result)]))})))
(sce/transition {:event :done.invoke.load-warnings
:cond (fn [env {:keys [foo] :as data}]
(= foo :bar)) ;; foo is nil here; should be :bar
:target :review-warnings}))1.4.0-RC12-SNAPSHOT. I added a ton of compliance tests as well and found a few other things worth fixing. There’s a new option for turning on strict mode, which fixes exception handling in expression nodes so that it short-circuits other elements in the block.
It's fine. I'm not using finalize, and the future thing was mainly an example i put in as a demo. Not surprising it isn't perfect, but finalize should be the same as the spec...i used their algorithm. You sure?
I'll throw analysis at it and see what i find
yeah, minor ordering issue on finalize…thanks for the catch. Putting regression tests in place. I will release an update shortly
Got it, thanks. Does it seem correct that I would create my own working memory store in order to address that last point about reacting to state transitions or data model changes in a session? e.g. something like
(defrecord LocalMemoryStore
[storage notify]
sp/WorkingMemoryStore
(get-working-memory [_ _ session-id]
(get @storage session-id))
(save-working-memory! [_ _ session-id wmem]
(swap! storage assoc session-id wmem)
;; Note this difference from the built-in LocalMemoryStore:
(notify session-id wmem))
(delete-working-memory! [_ _ session-id]
(swap! storage dissoc session-id)))
Or is there a better protocol to use for that?