Hacker News new | past | comments | ask | show | jobs | submit login
Generic FSMs in Erlang (learnyousomeerlang.com)
60 points by keyist on Dec 21, 2010 | hide | past | favorite | 20 comments



Good stuff. Compared to the more "popular" generic behaviours like gen_server, gen_fsm doesn't get a lot of attention. It's nice to see it added to your tutorial with this level of detail.


I've also gotten good mileage out of "gen_event"; I have a protocol with a generic connection layer, then a modular part where you claim you support "Module X", which is implemented as a gen_event plugin that first verifies you can do that, then allows this connection to have that module. It makes it really easy to write something relatively secure by ensuring you can get to certain capabilities if and only if you pass a certain check to validate you ability to have them. It's a good tradeoff between typing (as in, typing things on the keyboard) and security for me.


Author, here. I hope you enjoy.


Thanks, I am enjoying it. I'm working on a holiday project that includes writing an Erlang server that will need a FSM. Even though I've written many FSMs in other languages I'm new to Erlang, so it's great to have this nice guide. Glad I decided to peruse Hacker News before I started on the FSM implementation!


I prefer name-brand flying spaghetti monsters actually.


That what was I thought when I saw the title, too. However, from experience I realized that making any sort of comment alluding that without adding substantively to the discussion would certainly elicit sanctimonious shows of disapproval.

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.


Too bad the new "Erlang and OTP in Action" book leaves gen_fsm out of scope.

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).


"no on_entry / on_exit callbacks."

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".


As Erlang is Turing-complete, obviously you can code FSM in it. The question is, giving Erlang/OTP Soft-Realtime Telecom roots, why FSM primitives are so weak?

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.


[deleted]


Umm... didn't I spend something like three paragraphs not just saying that, but showing some code fragments too?


Yes, sorry. Misread you. Ignore my comment.


Hello all, the simplest answer for why we did not include the gen_fsm is that it is not used very often. In 11 years of Erlang programming I have only used it a handful of times. You can easily replicate it's behaviour with the gen_server in any case. Furthermore explaining it's use clearly turned out to be complicated. Rather than confuse readers, and add pages to an already lengthy book, we decided to skip it. If you clearly understand gen_server, superviser, application, and gen_event you will be able to pick up gen_fsm when you need to. If we do a second edition perhaps we can include the FSM as an appendix.

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.


Excellent book! I'm enjoying my copy and find it a great complement to the other two.

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.


What's wrong with spawning a separate process for the FSM? You will still typically have it linked in your supervisor hierarchy.


Sometimes you need just an FSM without attached process/thread with mailbox/queue (i.e. passive FSM). Then you have an API to dispatch events and query current state.

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;
  }


the fact that the gen_server is using a gen_fsm and that that requires two processes has little or no effect on the number of simultaneous clients you can have. Unless I am missing something, I am not quite sure why you are drawing that correlation.


When you handling millions of Comet clients per node - every byte counts. And even that processes are lightweight in Erlang, they still take about 300 bytes of memory + State.

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.


The authors don't care for it much :). I however argue its great for when it fits the problem. Like implementing a protocol like XMPP.


> Also, I'm not sure you can use gen_fsm sequentially by itself (i.e. without Erlang process).

Why would you want to do that ever?


Great stuff. I was just arguing on the ErlangCamp mailing list that the gen_fsm behaviour is great! I just sent this out to everyone.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: