Problem: Support chat bot style conversations with all possible branches/states configured at run time.
Is there an Phoenix/Elixir/Erlang library that can be leveraged to achieve a solution to above problem? FYI, I found http://erlang.org/doc/design_principles/statem.html but I am not sure if I am headed in correct direction with that.
:gen_statem / GenStateMachine are likely not the best fit for a machine configured at runtime, as they represent states and transitions with compiled Elixir code.
You could use them with an approach where they implemented a “generic machine” that interpreted the runtime configuration…
Some things to think about when picking implementations:
how durable is state? Does it survive the BEAM rebooting?
gen_statem doesn’t have any baked-in persistence, since it’s used for a lot of in-memory only things like tracking connections etc. Other solutions like ecto_state_machine are explicitly built on top of persistence.
can the machine initiate actions, or is it purely reactive to inputs?
gen_statem includes multiple timeout mechanisms and represents state as a running Erlang process. This can be really powerful for handling situations like “request a driver, then notify Operations if one isn’t assigned within 10 minutes”. On the other hand, something like ecto_state_machine only runs code when a state transition is happening.
But now that I think about it based on first principles approach. The solution I am looking for is really just a map i.e. a collection of states, possible events and transitions.
May be ecto_state_machine has some mechanism to easily create these maps dynamically. I don’t know enough about it.
I would appreciate experts opinion on possible solutions to the problem I am trying to solve.
You don’t want to handle dynamic state set with gen_statem or it’s related libraries because the states must be defined at compile time (there are two modes in gen_statem, one requires states to be defined as functions, the other as atoms).
You’ll just have to write your own state machine in gen_server. I think there are non-process bound state machine libraries that you could wrap in a gen_server.
Because you need the state machine to be modified at runtime you need the state machine described in data rather than compiled code. So look for pure data structures for which functions can be made to modify your state machine at runtime:
You can also use maps/structs and pass functions around to get somewhere quick.
I’m working on a forward-chaining workflow graph built on top of Libgraph for these kinds of use cases, but I’m trying to do some fun things with merkle-tree-like hashing to resolve workflows in a distributed setting and it’s not ready at all.