{"action":"created","issue":{"url":"https://api.github.com/repos/erlang/otp/issues/960","repository_url":"https://api.github.com/repos/erlang/otp","labels_url":"https://api.github.com/repos/erlang/otp/issues/960/labels{/name}","comments_url":"https://api.github.com/repos/erlang/otp/issues/960/comments","events_url":"https://api.github.com/repos/erlang/otp/issues/960/events","html_url":"https://github.com/erlang/otp/pull/960","id":132377016,"number":960,"title":"Raimo: New gen State Machine: OTP-13065","user":{"login":"RaimoNiskanen","id":189373,"avatar_url":"https://avatars.githubusercontent.com/u/189373?v=3","gravatar_id":"","url":"https://api.github.com/users/RaimoNiskanen","html_url":"https://github.com/RaimoNiskanen","followers_url":"https://api.github.com/users/RaimoNiskanen/followers","following_url":"https://api.github.com/users/RaimoNiskanen/following{/other_user}","gists_url":"https://api.github.com/users/RaimoNiskanen/gists{/gist_id}","starred_url":"https://api.github.com/users/RaimoNiskanen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/RaimoNiskanen/subscriptions","organizations_url":"https://api.github.com/users/RaimoNiskanen/orgs","repos_url":"https://api.github.com/users/RaimoNiskanen/repos","events_url":"https://api.github.com/users/RaimoNiskanen/events{/privacy}","received_events_url":"https://api.github.com/users/RaimoNiskanen/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":15,"created_at":"2016-02-09T10:03:06Z","updated_at":"2016-02-10T14:05:58Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/erlang/otp/pulls/960","html_url":"https://github.com/erlang/otp/pull/960","diff_url":"https://github.com/erlang/otp/pull/960.diff","patch_url":"https://github.com/erlang/otp/pull/960.patch"},"body":"**gen_statem** is a proposed new state machine engine in the gen_* framework that is intended to supersede _gen_fsm_ since many find it hardly usable.\r\n\r\nMajor benefits over _gen_fsm_:\r\n- All events are handled in the same place in the implementation for a given state\r\n- Selective receive is mimicked by an internal queue that allows retrying an event in a later state\r\n- An infinite state space is possible\r\n\r\nI have created a pull request of it to get external feedback.  We have started to convert some state machines here at OTP to use **gen_statem** and are so far fairly happy with it.\r\n\r\nBuild and read the documentation. Please write some state machines.\r\n\r\n/ Raimo Niskanen\r\n"},"comment":{"url":"https://api.github.com/repos/erlang/otp/issues/comments/182388931","html_url":"https://github.com/erlang/otp/pull/960#issuecomment-182388931","issue_url":"https://api.github.com/repos/erlang/otp/issues/960","id":182388931,"user":{"login":"RaimoNiskanen","id":189373,"avatar_url":"https://avatars.githubusercontent.com/u/189373?v=3","gravatar_id":"","url":"https://api.github.com/users/RaimoNiskanen","html_url":"https://github.com/RaimoNiskanen","followers_url":"https://api.github.com/users/RaimoNiskanen/followers","following_url":"https://api.github.com/users/RaimoNiskanen/following{/other_user}","gists_url":"https://api.github.com/users/RaimoNiskanen/gists{/gist_id}","starred_url":"https://api.github.com/users/RaimoNiskanen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/RaimoNiskanen/subscriptions","organizations_url":"https://api.github.com/users/RaimoNiskanen/orgs","repos_url":"https://api.github.com/users/RaimoNiskanen/repos","events_url":"https://api.github.com/users/RaimoNiskanen/events{/privacy}","received_events_url":"https://api.github.com/users/RaimoNiskanen/received_events","type":"User","site_admin":false},"created_at":"2016-02-10T14:05:58Z","updated_at":"2016-02-10T14:05:58Z","body":"@essen\r\n\r\nI think that if you remove features from `gen_statem` you will get `gen_server` or some small addition to it.\r\n\r\nThe features of `gen_statem` over `gen_server` as I see them are:\r\n* Handling all events in one specific function per state\r\n* Doing something close to selective receives i.e postponing events i.e an event queue\r\n* Enabling a non-finite state machine (vs `gen_fsm`)\r\n\r\nHandling timers/demonitors/unlinks as well as `insert_event`, `remove_event`, etc are needed due to the event queue.\r\n\r\n#### Regarding handling all events in one specific function per state\r\n\r\nWe agree on that the concept of having a single function per state is a great usability improvement, and that returning a list of commands is a good feature. Thank you for that!\r\n\r\n#### Regarding a non-finite state machine\r\n\r\nYou argue that by dropping `handle_event/5` we could drop the `State` argument making all state handling functions arity 4.  That would reserve the state name `code_change` or forcing some ugly quirk of the `code_change/4` function to change its arity.  Furthermore I have already while using `gen_statem` encountered this pattern:\r\n```\r\nstate1(info, event1, _PrevState, _State, _StateData) ->\r\n    do_something(event1),\r\n    {};\r\nstate1(Type, Content, PrevState, State, StateData) ->\r\n    handle_common_events(Type, Content, PrevState, State, SateData).\r\n```\r\nHere most of the events are handled in the state function, but some events benefit on using a common handling function, and that function might need `State`.  Sure you can write the literate state `state1` in the call, but then you are likely to get cut-and-paste errors.\r\n\r\nFurthermore we have already found use cases for a non-atom state, so I think it is a not unimportant feature.\r\n\r\nEnabling a non-finite state machine just has the consequence that the state function `handle_event/5` becomes special but only if you do not use a non-atom state which is up to you.\r\n\r\nTherefore I think the feature of a non-finite state machine is worth the small additional complexity.\r\n\r\n#### Regarding the event queue\r\n\r\nI think this is a killer feature of `gen_statem`.  You do not know what you are missing unless you have tried it.  I deem that there are many state machines out there written with `gen_server` that has some half-hearted event queue, some shady treatment of certain calls, or some creative interpretation of the specification just to get around the fact that you do not want to handle some events in all states but can not ignore them.\r\n\r\nThe basics are simple enough.  You `retry` an event and deal with it in a later state.  This is not as efficient as a true selective receive, but it should not be hard for the implementation to keep the number of postponed events small so I think correctness beats efficiency in this case.\r\n\r\nIf you remove the event queue from `gen_statem` you get `gen_server` with a little code for aggregating `handle_call`, `handle_cast` and `handle_info` into one function determined by one state field.  That is around 10 lines of code.\r\n\r\nTherefore I think the event queue is a really important feature that lifts the abstraction level far over what you easily can do with `gen_server`, and that without it there is no need for `gen_statem`.\r\n\r\n#### Removing stray messages from e.g `cancel_timer`\r\n\r\nYou could ditch this feature by stating that it is up to the state machine to ignore all such stray events.  But that would make it very hard to know if a stray `{timeout,Ref,Msg}` is actually one that you initiated yourself and then it became obsolete from one that you initiated erroneously.\r\n\r\nI think that it should be possible in a state machine implementation to know exactly which events that are truly unexpected, and therefore a reliable way to e.g cancel a timer and removing the stray message is needed.\r\n\r\nThe argument that most state machines for simplicity ignore all unknown messages e.g matching late timeout messages is not strong enough to not implement `{cancel_timer,Ref}` and its fellows.  If you want to write tight code you should be able to.\r\n\r\n\r\n#### Complexity\r\n\r\nI think most of the complexity is percieved from that I somewhat failed in the documentation, and that all the features are essential to make `gen_statem` worth using.\r\n"}}