Generic FSMs in Erlang
learnyousomeerlang.com
learnyousomeerlang.com
The key to getting away with that in this topic would be to make an insightful comment about finite state machines, and throw the FSM reference in at the beginning or the end. Extra bonus if you can skillfully weave a Flying Spaghetti Monster narrative into your comment, like a parable.
Anyway gen_fsm behavior is pretty weak comparing to FSM frameworks I used in C++: no on_entry / on_exit callbacks.
Also, I'm not sure you can use gen_fsm sequentially by itself (i.e. without Erlang process).
That's not too hard to fix; write your handle_event callback as something that observes the state, dispatches out to the "really_handle_event" function, observes the new state, and fires the desired callback, implemented however you want them to be. Ten reusable lines or so.
"Also, I'm not sure you can use gen_fsm sequentially by itself (i.e. without Erlang process)."
Not really much point, really. The gen_fsm itself is basically wrapping the state machine described there into a supervisor tree. If you don't want the supervisor tree, it is utterly trivial to write a state machine of your own in Erlang. It is one place where pattern matching excels. You want some sort of record indicating your state, then you can do things like:
-record(state, {name, val1, val2}).
event1(State=#state{name = initial}) -> handle...
event1(State=#state{name = connecting}) -> handle...
event1(State=#state{name = connected}) -> handle...
event2(State=#state{name = initial}) -> handle...
and so on. A little more plumbing is necessary to manage returning the new state but that's the basic idea, which is just about the only other thing you "get" from gen_fsm "for free".Deleted comment
From a good FSM library I would expect following:
1. Specify human readable STT (State Transition Table)
2. For each event/state combination specify if it's valid or should be ignored, etc.
3. On entry / on exit for each state.
4. Masking/unmasking of events.
gen_fsm as it today do not provide much value over gen_server.The gen_fsm implementation is elegant and I do recommend taking a look at it but you probably won't find yourself using it all too often. At the end of the day we wanted to write a book that prepared people for real world Erlang situations and the gen_fsm does not weigh heavily in those scenarios.
riak_kv uses gen_fsm, so people looking for a real world use case could check out their usage: https://github.com/basho/riak_kv/blob/master/src/riak_kv_get... https://github.com/basho/riak_kv/blob/master/src/riak_kv_put...
I'm a relatively new to Erlang, but being familiar with gen_server made it pretty easy to understand the gen_fsm docs and figure out what was going on. I don't see the exclusion as a big deal.
For example if you have gen_server using FSM, with gen_fsm you will have 2 processes. If there was passive FSM module, you would save 1 process and handle twice as many simultaneous clients.
Some examples from boost statechart library:
http://www.boost.org/doc/libs/1_34_0/libs/statechart/doc/tut...
#include <boost/statechart/transition.hpp>
// ...
int main()
{
StopWatch myWatch;
myWatch.initiate();
myWatch.process_event( EvStartStop() );
myWatch.process_event( EvStartStop() );
myWatch.process_event( EvStartStop() );
myWatch.process_event( EvReset() );
return 0;
}FSM is abstract concept. When it's wrapped into Actor it's something else and not pure FSM. gen_fsm is more similar to Rational Rose "Capsule" design pattern, except that Capsule may have several message queues, while gen_fsm has only one. You can also mask/unmask events in Capsule.
When many years ago I first learned Erlang I thought that every process will have built-in FSM as 1st class citizen (this is what I expected from programming language designed by Telecom company).
Also what I would like from FSM module, is to be able just to specify state transition table (STT) including on entry/exit. Then the callback functions only handle state transition logic. Currently gen_fsm is very verbose for huge FSMs and I sure it's easy to introduce bugs when coding STTs.
I think the reason most people don't use gen_fsm is because it's very basic and verbose, not because there is no need in built-in FSM behaviour in OTP.
Why would you want to do that ever?