I love state machines, and I use them in real world scenarios at least once a year.
The more I'm using them, in different languages or different situations, the more I get to "it's just an if" conclusion.
In this HN post about xstate, there's a minimalistic implementation in swift which I find a very nice distillation of how I use state machines usually.
switch [current-state, event ]
case ['foo', 'bar' ] -> ...
And that's 90% of it.
I've used ruby's statemachine, clojure's tilakone and clj-statecharts (there's also this new maestro lib which I haven't used yet), and home made lua ones.
But the essence is, having a clear distinction of where am I, what I get, and what do I have to do. For the "what do I have to do", here's what I usually do:
- Updating the state of an object (usually the one bound to the state machine) is fine.
- I tend to run actions only for things that need to be immediate. Sending an alert in a UI has to be synchronous I guess.
- But if I can, I like to run some idempotent process that will run the actions. That process should notice that the state machine has stuff to do. That process is the one that has the knowledge of actions that should happen in XYZ situation. This process runs them, and logs them somewhere, so that next polling iteration doesn't redo them. These actions are things like "cancel a subscription after being unpaid for x days". The state machine will set the status to "unpaid", and set the cancel-at, but the polling process will check for "unpaid" subscriptions with cancel-at<now() that are still running, and kill them for good.
Further reading on xstate and fsm and one/two/three level fsms:
- http://www.findinglisp.com/blog/archives/2004_06_01_archive.html
- https://www.industriallogic.com/patterns/P22.pdf
- https://hillside.net/plop/plop2003/Papers/Adamczyk-State-Machine.pdf
No hay comentarios:
Publicar un comentario