Blogged about the motivation and reasoning behind my idea to create yet another FSM library. Long story short: I did it in a proper way
very interesting. the example at the end could be a bit more ‘real world’ as i’ve only heard about FSM used in games. What do you use it for?
I wanted something, that is error-prone and easy to grasp.
You probably wanted to say “not error-prone”?
The real-world example would have not fit the page
We in Fintech use FSMs for literally everything. Consider a long-lived “transaction” when the client puts an order and saves it, in ten days comes and tries to execute it. We are to contact a bank, receive a response and either trade or contact client for why it could not be traded etc. It’s like eventlog, but for the state of the object. We must prevent the client from the attempt to execute incomplete order, or to re-execute the order rejected by bank without introducing any changes, or like. One FSM obsoletes dozens of if
conditional statements.
Of course, thanks, fixed.
I follow your blog already; the contents are always superb. May I suggest you to add the tags in your feed? You already have a nice tagging system; it’s a shame that this tagging system is not exposed through your feed. Since you are using Atom, the standard format shall be:
<category term="elixir" />
...
We use state machines all the time in Control Systems. If you want to run a piece of equipment or an entire plant, you need a well defined set of states to step through (or out of).
In the BEAM world, connections to things can be handled with gen_statem
nicely too.
I wrote this library which seems similar:
Ouch, indeed. Great one! I googled and grepped hex.pm but somehow failed to nail it down. Two independent solutions so similar is a hint it’s a proper approach.
There is one significant difference, though. The definition made of PlantUML allows a no-cost visualization, after I’ve done Make embedded FSM description visible in VSCode · Issue #2 · am-kantox/finitomata · GitHub, the diagram would be transparently visible in VSCode, and the mix
task to generate the diagram will also be available soon.
Also, by hiding all the OTP stuff under the hood and exposing callbacks only, I hope I made it more beginners-friendly.
It’s too low-level and has its own drawbacks though. I find gen_statem
somewhat alongside Agent
in Elixir. Syntactic sugar does not actually bring any significant improvement over gen_server
, but it must be only me.
Thanks for heads up! Done.
Turned out, I’ve already written about another real example: webhooks.
I think there’s probably a smart way to use mermaid (which is easy to ship with exdoc now) instead. Plantuml is kind of tricky syntax and while it’s open source, it’s really hard to install and put into a pipeline :P. Though at work I forced the database tables to be plantuml documented by creating a compile-time check.
Wow. Thanks! Somehow I missed this possibility. Will surely support it alongside PlantUML. BTW, the compile-time check is already implemented in the library during definition parsing.
FWIW, I have implemented mermaid
support. This is how the documentation for the generated module looks when mermaid
syntax is used. Kudos and many thanks!
This reminds me of the FSM framework I wrote for usage in my own project.
I did however not resort to parsing a description string, I used some macros instead.
defmacro __using__(_opts) do
quote do
Module.register_attribute(__MODULE__, :transitions, accumulate: true)
import unquote(__MODULE__)
# ensure our additional functions are added only right before compilation
# after all module attribute values have been fully determined
@before_compile unquote(__MODULE__)
end
end
defmacro __before_compile__(_env) do
quote do
def transitions, do: @transitions
end
end
defmacro transition(list) do
# [...] omitted some checks for required fields for example
quote do
@transitions unquote(list)
end
end
You can then add transitions like this:
use # "<module name>"
transition(state: :idle, in: :start, next: :wait)
transition(state: :wait, in: :continue, end: :pass)
transition(state: :end, in: :finish)
I ended up supporting waiting for different incoming messages, a join, end states, and “guard functions” that can further refine if a transition is taken.
The resulting list of transitions then ends up in a module attribute I can read and process.
(The part of handler functions or callbacks is not shown as it is not specific to describing the transition table. I defaulted to handle/2
as a default function for incoming functions, and allowed a user-defined handler name if needed that could be defined differently for every single transition.)
Great! But in my understanding, the textual representation is easier to grasp and it produces a diagram in docs for free.
these examples are great. this opens up a whole new section in my head. thanks!
First, really great package, @mudasobwa. Easy to use and intuitive. Thank you for this contribution to the Elixir ecosystem.
Some days ago I tried to implemented a FSM to manage the setup of an application, but then I realized that maybe is not the proper use case.
Aside from the examples given in Finitomata’s documentation, have you got some application example with Finitomata implemented? Just to learn until I have the necessary time to implement some ideas I have.
Thanks in advance!
There is finitomata/examples/ecto_intergation at main · am-kantox/finitomata · GitHub non-official take on integration with ecto
(which would not be the best implementation, but still.)
Aside from that, all other real-world examples I have I cannot actually share. I will try to come up with a better more sophisticated example, thanks.
Thank you very much, @mudasobwa! I must be going blind because I really didn’t see the examples
directory in Finitomata’s repository . Really useful example.