@drewverlee I'm a bit late to the conversation here, but what you described above sounds similar to an event driven FSM I wrote a while back. We used it in one of our applications for a time that had a complex business logic workflow and the FSM abstraction made it easier to comprehend/represent. We ended up scrapping that part of the application for other reasons, but the code remains in one of our OSS projects: https://github.com/LiaisonTechnologies/proletariat/blob/master/src/proletariat/fsm.clj. It has a good bit of documentation. Might at least generate some ideas...
@manderson very interesting i’ll take a look. The description seems close to what i imagine. I’m curious why i dont see things modeled this way more often. I want a more declarative way to model the possible states an application can be in.
Yep, that's what lead me to that approach and it actually made for really easy to visualize code (and made me wonder if I was missing something because it isn't more prevalent). One problem is that it's hard to test if your events have side effects (eg: calls to a DB), so I added a multi-mock namespace in the same repo above for mocking multi-methods. It's not ideal but served its purpose.
> One problem is that it’s hard to test if your events have side effects (eg: calls to a DB), so I added a multi-mock namespace in the same repo above for mocking multi-methods.
This is interesting but i’m not sure i understand. I was thinking about having the state flow processor put events on a event log. In cases where processing these events meant triggering a side effect I would declear just record the intent to fire the site effect. Here display board would print a screen. I’m not actual going to trigger that action when testing.
[{:action :display-board*}]
Here get-user-input* would potentially read input from the command line (or wait for input from a client/browser). In testing, I would mock the side-effect (clojure spec has facilities for this) part to just generate data mostly valid but some invalid.
[{:action :get-player-input*}]
Does that make sense? how does that compare with what you were suggesting?
In my case, I was using the fsm/auto-event
multi-method to determine what processing step to take next. In our system, we were receiving a document off a queue and then making some calls to different downstream systems based on some business logic. We would then handle the response and invoke a "success" path or "exception" path (by returning the next step in the FSM as a response). In testing, I needed a way to test the different scenarios, so I used multi-mock to mock the outputs of the fsm/auto-event
multi-methods. Looking back, I probably should have organized the system a bit differently so that I could have replaced the side-effect calls more easily, but that was what I came up with at the time (this was very early days of spec).
I think your approach sounds good. As I said, I should have kept the side effects at the edges and allowed an easy way to mock results for testing
Thanks for the response. I’ll have to dig into your library. My system at the moment is self syncronsis in nature (or at least i can make it that way). So i think it varies for your setup in that way. I also realized that because i dont have multiple subscribers to my events (the state log) i dont really need to record that the actions were completed. When i’m done i’m going to turn this adventure into a mini blog post mainly to facilitate feedback 🙂. Its just a simple tic tac toe game, bu i like trying to solve the ugly parts of small problems because i figure if i cant do it on the small things i have no hope when things get bigger.
luna-lang is all around that concept
I’m going to give Luna Lang a try. I dont think i would want to write my programs complete in (what im calling) a state flow pattern. Just the high level parts and probably only in cases where things are very stateful and can loop back on themselves conceptually (like game-over -> reset-game -> etc…)